diff --git a/CImg.h b/CImg.h new file mode 100644 index 0000000..5306167 --- /dev/null +++ b/CImg.h @@ -0,0 +1,46179 @@ +/* + # + # File : CImg.h + # ( C++ header file ) + # + # Description : The C++ Template Image Processing Toolkit. + # This file is the main component of the CImg Library project. + # ( http://cimg.sourceforge.net ) + # + # Project manager : David Tschumperle. + # ( http://tschumperle.users.greyc.fr/ ) + # + # A complete list of contributors is available in file 'README.txt' + # distributed within the CImg package. + # + # Licenses : This file is 'dual-licensed', you have to choose one + # of the two licenses below to apply. + # + # 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. + # +*/ + +// Set version number of the library. +#ifndef cimg_version +#define cimg_version 156 + +/*----------------------------------------------------------- + # + # Test and possibly auto-set CImg configuration variables + # and include required headers. + # + # If you find that the default configuration variables are + # not adapted to your system, you can override their values + # before including the header file "CImg.h" + # (use the #define directive). + # + ------------------------------------------------------------*/ + +// Include standard C++ headers. +// This is the minimal set of required headers to make CImg-based codes compile. +#include +#include +#include +#include +#include +#include +#include + +// Detect/configure OS variables. +// +// Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies). +// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). +// '2' for Microsoft Windows. +// (auto-detection is performed if 'cimg_OS' is not set by the user). +#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: Invalid configuration variable 'cimg_OS'. +#error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). +#endif + +// Disable silly warnings on some Microsoft VC++ compilers. +#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 +#endif + +// Include OS-specific headers. +#if cimg_OS==1 +#include +#include +#include +#elif cimg_OS==2 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#include +#include +#define cimg_snprintf _snprintf +#define cimg_vsnprintf _vsnprintf +#endif +#ifndef cimg_snprintf +#include +#define cimg_snprintf snprintf +#define cimg_vsnprintf vsnprintf +#endif + +// Configure filename separator. +// +// Filename separator is set by default to '/', except for Windows where it is '\'. +#ifndef cimg_file_separator +#if cimg_OS==2 +#define cimg_file_separator '\\' +#else +#define cimg_file_separator '/' +#endif +#endif + +// Configure verbosity of output messages. +// +// Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode). +// '1' to output library messages on the console. +// '2' to output library messages on a basic dialog window (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 on VT100-compatible terminals. +#ifndef cimg_verbosity +#define cimg_verbosity 2 +#elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) +#error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. +#error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). +#endif + +// Configure display framework. +// +// Define 'cimg_display' to: '0' to disable display capabilities. +// '1' to use the X-Window framework (X11). +// '2' to use the Microsoft GDI32 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) +#error CImg Library: Configuration variable 'cimg_display' is badly defined. +#error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }). +#endif + +// Include display-specific headers. +#if cimg_display==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#endif +#ifndef cimg_appname +#define cimg_appname "CImg" +#endif + +// Configure OpenMP support. +// (http://www.openmp.org) +// +// Define 'cimg_use_openmp' to enable OpenMP support. +// +// OpenMP directives may be used in a (very) few CImg functions to get +// advantages of multi-core CPUs. +#ifdef cimg_use_openmp +#include "omp.h" +#define _cimg_static +#else +#define _cimg_static static +#endif + +// Configure OpenCV support. +// (http://opencv.willowgarage.com/wiki/) +// +// Define 'cimg_use_opencv' to enable OpenCV support. +// +// OpenCV library may be used to access images from cameras +// (see method 'CImg::load_camera()'). +#ifdef cimg_use_opencv +#ifdef True +#undef True +#define _cimg_redefine_True +#endif +#ifdef False +#undef False +#define _cimg_redefine_False +#endif +#include +#include "cv.h" +#include "highgui.h" +#endif + +// Configure LibPNG support. +// (http://www.libpng.org) +// +// Define 'cimg_use_png' to enable LibPNG support. +// +// PNG library may be used to get a native support of '.png' files. +// (see methods 'CImg::{load,save}_png()'. +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif + +// Configure LibJPEG support. +// (http://en.wikipedia.org/wiki/Libjpeg) +// +// Define 'cimg_use_jpeg' to enable LibJPEG support. +// +// JPEG library may be used to get a native support of '.jpg' files. +// (see methods 'CImg::{load,save}_jpeg()'). +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +#include "setjmp.h" +} +#endif + +// Configure LibTIFF support. +// (http://www.libtiff.org) +// +// Define 'cimg_use_tiff' to enable LibTIFF support. +// +// TIFF library may be used to get a native support of '.tif' files. +// (see methods 'CImg[List]::{load,save}_tiff()'). +#ifdef cimg_use_tiff +extern "C" { +#include "tiffio.h" +} +#endif + +// Configure LibMINC2 support. +// (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) +// +// Define 'cimg_use_minc2' to enable LibMINC2 support. +// +// MINC2 library may be used to get a native support of '.mnc' files. +// (see methods 'CImg::{load,save}_minc2()'). +#ifdef cimg_use_minc2 +#include "minc_io_simple_volume.h" +#include "minc_1_simple.h" +#include "minc_1_simple_rw.h" +#endif + +// Configure FFMPEG support. +// (http://www.ffmpeg.org) +// +// Define 'cimg_use_ffmpeg' to enable FFMPEG lib support. +// +// Avcodec and Avformat libraries from FFMPEG may be used +// to get a native support of various video file formats. +// (see methods 'CImg[List]::load_ffmpeg()'). +#ifdef cimg_use_ffmpeg +#if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C) +#warning "__STDC_CONSTANT_MACROS has to be defined before including , this file will probably not compile." +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS // ...or stdint.h wont' define UINT64_C, needed by libavutil +#endif +extern "C" { +#include "avformat.h" +#include "avcodec.h" +#include "swscale.h" +} +#endif + +// Configure Zlib support. +// (http://www.zlib.net) +// +// Define 'cimg_use_zlib' to enable Zlib support. +// +// Zlib library may be used to allow compressed data in '.cimgz' files +// (see methods 'CImg[List]::{load,save}_cimg()'). +#ifdef cimg_use_zlib +extern "C" { +#include "zlib.h" +} +#endif + +// Configure Magick++ support. +// (http://www.imagemagick.org/Magick++) +// +// Define 'cimg_use_magick' to enable Magick++ support. +// +// Magick++ library may be used to get a native support of various image file formats. +// (see methods 'CImg::{load,save}()'). +#ifdef cimg_use_magick +#include "Magick++.h" +#endif + +// Configure FFTW3 support. +// (http://www.fftw.org) +// +// Define 'cimg_use_fftw3' to enable libFFTW3 support. +// +// FFTW3 library may be used to efficiently compute the Fast Fourier Transform +// of image data, without restriction on the image size. +// (see method 'CImg[List]::FFT()'). +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +// Configure LibBoard support. +// (http://libboard.sourceforge.net/) +// +// Define 'cimg_use_board' to enable Board support. +// +// Board library may be used to draw 3d objects in vector-graphics canvas +// that can be saved as '.ps' or '.svg' files afterwards. +// (see method 'CImg::draw_object3d()'). +#ifdef cimg_use_board +#ifdef None +#undef None +#define _cimg_redefine_None +#endif +#include "Board.h" +#endif + +// Configure OpenEXR support. +// (http://www.openexr.com/) +// +// Define 'cimg_use_openexr' to enable OpenEXR support. +// +// OpenEXR library may be used to get a native support of '.exr' files. +// (see methods 'CImg::{load,save}_exr()'). +#ifdef cimg_use_openexr +#include "ImfRgbaFile.h" +#include "ImfInputFile.h" +#include "ImfChannelList.h" +#include "ImfMatrixAttribute.h" +#include "ImfArray.h" +#endif + +// Lapack configuration. +// (http://www.netlib.org/lapack) +// +// Define 'cimg_use_lapack' to enable LAPACK support. +// +// Lapack library may be used in several CImg methods to speed up +// matrix computations (eigenvalues, inverse, ...). +#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*); + extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*); + extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*); +} +#endif + +// Check if min/max/PI macros are defined. +// +// CImg does not compile if macros 'min', 'max' or 'PI' are defined, +// because it redefines functions min(), max() and const variable PI in the cimg:: namespace. +// so it '#undef' these macros if necessary, and restore them to reasonable +// values at the end of this file. +#ifdef min +#undef min +#define _cimg_redefine_min +#endif +#ifdef max +#undef max +#define _cimg_redefine_max +#endif +#ifdef PI +#undef PI +#define _cimg_redefine_PI +#endif + +// Define 'cimg_library' namespace suffix. +// +// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work +// with several versions of the library at the same time. +#ifdef cimg_namespace_suffix +#define __cimg_library_suffixed(s) cimg_library_##s +#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) +#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) +#else +#define cimg_library_suffixed cimg_library +#endif + +/*------------------------------------------------------------------------------ + # + # Define user-friendly macros. + # + # These CImg macros are prefixed by 'cimg_' and can be used safely in your own + # code. They are useful to parse command line options, or to write image loops. + # + ------------------------------------------------------------------------------*/ + +// Macros to define program usage, and retrieve command line arguments. +#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) +#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage) +#define cimg_argument(pos) cimg_library_suffixed::cimg::argument(pos,argc,argv) +#define cimg_argument1(pos,s0) cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0) +#define cimg_argument2(pos,s0,s1) cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1) +#define cimg_argument3(pos,s0,s1,s2) cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2) +#define cimg_argument4(pos,s0,s1,s2,s3) cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) +#define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) +#define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library_suffixed::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) +#define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library_suffixed::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) +#define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library_suffixed::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) +#define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) + +// Macros to 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,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_p1##x,y,z,c), \ + I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), \ + I[8] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get4x4(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_n2##x,_p1##y,z,c), \ + I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), \ + I[8] = (T)(img)(_p1##x,_n1##y,z,c), I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), I[15] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get5x5(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), \ + I[8] = (T)(img)(_n1##x,_p1##y,z,c), I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \ + I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), I[15] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[20] = (T)(img)(_p2##x,_n2##y,z,c), I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get6x6(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), \ + I[8] = (T)(img)(x,_p1##y,z,c), I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), I[15] = (T)(img)(_n1##x,y,z,c), \ + I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), \ + I[20] = (T)(img)(x,_n1##y,z,c), I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), I[27] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[32] = (T)(img)(x,_n3##y,z,c), I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get7x7(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \ + I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), \ + I[8] = (T)(img)(_p2##x,_p2##y,z,c), I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), I[15] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), \ + I[20] = (T)(img)(_n3##x,_p1##y,z,c), I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \ + I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), I[27] = (T)(img)(_n3##x,y,z,c), \ + I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), \ + I[32] = (T)(img)(_n1##x,_n1##y,z,c), I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \ + I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), I[39] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), \ + I[44] = (T)(img)(_p1##x,_n3##y,z,c), I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[48] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get8x8(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \ + I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), \ + I[8] = (T)(img)(_p3##x,_p2##y,z,c), I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \ + I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), I[15] = (T)(img)(_n4##x,_p2##y,z,c), \ + I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), \ + I[20] = (T)(img)(_n1##x,_p1##y,z,c), I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), I[27] = (T)(img)(x,y,z,c), \ + I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), \ + I[32] = (T)(img)(_p3##x,_n1##y,z,c), I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \ + I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), I[39] = (T)(img)(_n4##x,_n1##y,z,c), \ + I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), \ + I[44] = (T)(img)(_n1##x,_n2##y,z,c), I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), I[51] = (T)(img)(x,_n3##y,z,c), \ + I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), \ + I[56] = (T)(img)(_p3##x,_n4##y,z,c), I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \ + I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), I[63] = (T)(img)(_n4##x,_n4##y,z,c); + +#define cimg_get9x9(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), I[3] = (T)(img)(_p1##x,_p4##y,z,c), \ + I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), \ + I[8] = (T)(img)(_n4##x,_p4##y,z,c), I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \ + I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), I[15] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), \ + I[20] = (T)(img)(_p2##x,_p2##y,z,c), I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), I[27] = (T)(img)(_p4##x,_p1##y,z,c), \ + I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), \ + I[32] = (T)(img)(_n1##x,_p1##y,z,c), I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), I[39] = (T)(img)(_p1##x,y,z,c), \ + I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), \ + I[44] = (T)(img)(_n4##x,y,z,c), I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), I[51] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), \ + I[56] = (T)(img)(_p2##x,_n2##y,z,c), I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), I[63] = (T)(img)(_p4##x,_n3##y,z,c), \ + I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), \ + I[68] = (T)(img)(_n1##x,_n3##y,z,c), I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \ + I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), I[75] = (T)(img)(_p1##x,_n4##y,z,c), \ + I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), \ + I[80] = (T)(img)(_n4##x,_n4##y,z,c) + +#define cimg_get2x2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +#define cimg_get3x3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), \ + I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), I[5] = (T)(img)(_n1##x,y,_p1##z,c), \ + I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), \ + I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), I[11] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), I[14] = (T)(img)(_n1##x,y,z,c), \ + I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), I[17] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), \ + I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), I[23] = (T)(img)(_n1##x,y,_n1##z,c), \ + I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +// Macros to perform various image loops. +// +// These macros are simpler to use than loops with C++ iterators. +#define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs) +#define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; ) +#define cimg_foroff(img,off) for (unsigned long off = 0, _max##off = (img).size(); off<_max##off; ++off) + +#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_forC(img,c) cimg_for1((img)._spectrum,c) +#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_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x) +#define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y) +#define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y) +#define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z) +#define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z) +#define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z) + +#define cimg_rof1(bound,i) for (int i = (int)(bound)-1; i>=0; --i) +#define cimg_rofX(img,x) cimg_rof1((img)._width,x) +#define cimg_rofY(img,y) cimg_rof1((img)._height,y) +#define cimg_rofZ(img,z) cimg_rof1((img)._depth,z) +#define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c) +#define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x) +#define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x) +#define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y) +#define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x) +#define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y) +#define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z) +#define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y) +#define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y) +#define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z) +#define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z) +#define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(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_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c) +#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_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) 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_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z) +#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_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) +#define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) +#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_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c) +#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_insideXYZC(img,x,y,z,c,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z) + +#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_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c) +#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_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c) +#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_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c) +#define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c) +#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_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c) +#define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c) +#define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c) +#define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) +#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_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c) +#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_borderXYZC(img,x,y,z,c,n) \ + cimg_for_outXYZC(img,n,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),(img)._spectrum-1-(n),x,y,z,c) + +#define cimg_for_spiralXY(img,x,y) \ + for (int x = 0, y = 0, _n1##x = 1, _n1##y = (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_for2C(img,c) cimg_for2((img)._spectrum,c) +#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_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y) +#define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z) +#define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z) +#define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) 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_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c) +#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_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) 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_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z) +#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_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for3C(img,c) cimg_for3((img)._spectrum,c) +#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_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y) +#define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z) +#define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z) +#define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) 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_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c) +#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_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) 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_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z) +#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_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for4C(img,c) cimg_for4((img)._spectrum,c) +#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_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y) +#define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z) +#define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z) +#define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) 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_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c) +#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_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) 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_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z) +#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_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for5C(img,c) cimg_for5((img)._spectrum,c) +#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_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y) +#define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) +#define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z) +#define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z) +#define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) 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_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c) +#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_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) 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_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z) +#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_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for6C(img,c) cimg_for6((img)._spectrum,c) +#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_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x) +#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) +#define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y) +#define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z) +#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) +#define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z) +#define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z) +#define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) 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_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c) +#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_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) 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_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z) +#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_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for7C(img,c) cimg_for7((img)._spectrum,c) +#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_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x) +#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) +#define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y) +#define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z) +#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) +#define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z) +#define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z) +#define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) 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_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c) +#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_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) 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_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z) +#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_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for8C(img,c) cimg_for8((img)._spectrum,c) +#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_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x) +#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) +#define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y) +#define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z) +#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) +#define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z) +#define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z) +#define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) 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_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c) +#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_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) 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_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z) +#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_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#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_for9C(img,c) cimg_for9((img)._spectrum,c) +#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_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x) +#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) +#define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y) +#define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z) +#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) +#define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z) +#define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z) +#define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) 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_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c) +#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_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) 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_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z) +#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_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),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,c,I,T) \ + cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + x+1>=(int)(img)._width?(img).width()-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),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,c,I,T) \ + 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] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[4] = (T)(img)(x,y,z,c)), \ + (I[7] = (T)(img)(x,_n1##y,z,c)), \ + x+1>=(int)(img)._width?(img).width()-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),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,c,I,T) \ + cimg_for4((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width()-1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = I[5] = (T)(img)(0,y,z,c)), \ + (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width()-1:2); \ + (_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),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,c,I,T) \ + 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?(img).width()-1:x+1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = (T)(img)(_p1##x,y,z,c)), \ + (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[5] = (T)(img)(x,y,z,c)), \ + (I[9] = (T)(img)(x,_n1##y,z,c)), \ + (I[13] = (T)(img)(x,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x+2>=(int)(img)._width?(img).width()-1:x+2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),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,c,I,T) \ + cimg_for5((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width()-1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \ + (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \ + (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width()-1:2); \ + (_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),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,c,I,T) \ + 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?(img).width()-1:x+1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[10] = (T)(img)(_p2##x,y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[11] = (T)(img)(_p1##x,y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[7] = (T)(img)(x,_p1##y,z,c)), \ + (I[12] = (T)(img)(x,y,z,c)), \ + (I[17] = (T)(img)(x,_n1##y,z,c)), \ + (I[22] = (T)(img)(x,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x+2>=(int)(img)._width?(img).width()-1:x+2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),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,c,I,T) \ + cimg_for6((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width()-1:1, \ + _n2##x = 2>=(img)._width?(img).width()-1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \ + (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \ + (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \ + (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width()-1:3); \ + (_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),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,c,I,T) \ + 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?(img).width()-1:x+1, \ + _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p2##x,y,z,c)), \ + (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_p1##x,y,z,c)), \ + (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[8] = (T)(img)(x,_p1##y,z,c)), \ + (I[14] = (T)(img)(x,y,z,c)), \ + (I[20] = (T)(img)(x,_n1##y,z,c)), \ + (I[26] = (T)(img)(x,_n2##y,z,c)), \ + (I[32] = (T)(img)(x,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x+3>=(int)(img)._width?(img).width()-1:x+3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),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,c,I,T) \ + cimg_for7((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width()-1:1, \ + _n2##x = 2>=(img)._width?(img).width()-1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \ + (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \ + (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \ + (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \ + (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \ + (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width()-1:3); \ + (_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),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,c,I,T) \ + 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?(img).width()-1:x+1, \ + _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[21] = (T)(img)(_p3##x,y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[22] = (T)(img)(_p2##x,y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[23] = (T)(img)(_p1##x,y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[10] = (T)(img)(x,_p2##y,z,c)), \ + (I[17] = (T)(img)(x,_p1##y,z,c)), \ + (I[24] = (T)(img)(x,y,z,c)), \ + (I[31] = (T)(img)(x,_n1##y,z,c)), \ + (I[38] = (T)(img)(x,_n2##y,z,c)), \ + (I[45] = (T)(img)(x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x+3>=(int)(img)._width?(img).width()-1:x+3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),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,c,I,T) \ + cimg_for8((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width()-1:1, \ + _n2##x = 2>=((img)._width)?(img).width()-1:2, \ + _n3##x = 3>=((img)._width)?(img).width()-1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \ + (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \ + (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \ + (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \ + (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \ + (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \ + (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width()-1:4); \ + (_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),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,c,I,T) \ + 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>=(img).width()?(img).width()-1:x+1, \ + _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \ + _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[24] = (T)(img)(_p3##x,y,z,c)), \ + (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_p2##x,y,z,c)), \ + (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_p1##x,y,z,c)), \ + (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[11] = (T)(img)(x,_p2##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,z,c)), \ + (I[27] = (T)(img)(x,y,z,c)), \ + (I[35] = (T)(img)(x,_n1##y,z,c)), \ + (I[43] = (T)(img)(x,_n2##y,z,c)), \ + (I[51] = (T)(img)(x,_n3##y,z,c)), \ + (I[59] = (T)(img)(x,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x+4>=(img).width()?(img).width()-1:x+4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),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,c,I,T) \ + 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)?(img).width()-1:1, \ + _n2##x = 2>=((img)._width)?(img).width()-1:2, \ + _n3##x = 3>=((img)._width)?(img).width()-1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \ + (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \ + (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \ + (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \ + (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \ + (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \ + (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \ + (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width()-1:4); \ + (_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),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,c,I,T) \ + 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>=(img).width()?(img).width()-1:x+1, \ + _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \ + _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \ + (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \ + (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \ + (I[36] = (T)(img)(_p4##x,y,z,c)), \ + (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \ + (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \ + (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \ + (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \ + (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[37] = (T)(img)(_p3##x,y,z,c)), \ + (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \ + (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[38] = (T)(img)(_p2##x,y,z,c)), \ + (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[39] = (T)(img)(_p1##x,y,z,c)), \ + (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[4] = (T)(img)(x,_p4##y,z,c)), \ + (I[13] = (T)(img)(x,_p3##y,z,c)), \ + (I[22] = (T)(img)(x,_p2##y,z,c)), \ + (I[31] = (T)(img)(x,_p1##y,z,c)), \ + (I[40] = (T)(img)(x,y,z,c)), \ + (I[49] = (T)(img)(x,_n1##y,z,c)), \ + (I[58] = (T)(img)(x,_n2##y,z,c)), \ + (I[67] = (T)(img)(x,_n3##y,z,c)), \ + (I[76] = (T)(img)(x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x+4>=(img).width()?(img).width()-1:x+4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),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,c,I,T) \ + cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + (I[4] = (T)(img)(0,y,_n1##z,c)), \ + (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),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,c,I,T) \ + 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] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + (I[4] = (T)(img)(x,y,_n1##z,c)), \ + (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x+1>=(int)(img)._width?(img).width()-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),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,c,I,T) \ + cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \ + (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \ + (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \ + (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \ + (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),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,c,I,T) \ + 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] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \ + (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,y,z,c)), \ + (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \ + (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \ + (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \ + (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \ + (I[4] = (T)(img)(x,y,_p1##z,c)), \ + (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \ + (I[10] = (T)(img)(x,_p1##y,z,c)), \ + (I[13] = (T)(img)(x,y,z,c)), \ + (I[16] = (T)(img)(x,_n1##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \ + (I[22] = (T)(img)(x,y,_n1##z,c)), \ + (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x+1>=(int)(img)._width?(img).width()-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),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 cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l) +#define cimglist_for_in(list,l0,l1,l) \ + for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width-1; l<=_max##l; ++l) + +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn + +// Macros used to display error messages when exceptions are thrown. +// You should not use these macros is your own code. +#define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::" +#define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']' +#define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::" +#define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type() +#define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::" +#define cimglist_instance _width,_allocated_width,_data,pixel_type() + +/*------------------------------------------------ + # + # + # Define cimg_library:: namespace + # + # + -------------------------------------------------*/ +//! Contains all classes and functions of the \CImg library. +/** + This namespace is defined to avoid functions and class names collisions + that could happen with the inclusion 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 objects afterwards. +**/ +namespace cimg_library_suffixed { + + // Declare the four classes of the CImg Library. + template struct CImg; + template struct CImgList; + struct CImgDisplay; + struct CImgException; + + // Declare cimg:: namespace. + // This is an uncomplete namespace definition here. It only contains some + // necessary stuffs to ensure a correct declaration order of the classes and functions + // defined afterwards. + namespace cimg { + + // Define ascii sequences for colored terminal output. +#ifdef cimg_use_vt100 + const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; + const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; + const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; + const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; + const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; + const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; + const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; + const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; + const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; + const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; +#else + const char t_normal[] = { 0 }; + const char *const t_black = cimg::t_normal, + *const t_red = cimg::t_normal, + *const t_green = cimg::t_normal, + *const t_yellow = cimg::t_normal, + *const t_blue = cimg::t_normal, + *const t_magenta = cimg::t_normal, + *const t_cyan = cimg::t_normal, + *const t_white = cimg::t_normal, + *const t_bold = cimg::t_normal, + *const t_underscore = cimg::t_normal; +#endif + + inline std::FILE* output(std::FILE *file=0); + inline void info(); + + //! Avoid warning messages due to unused parameters. Do nothing actually. + template + inline void unused(const T&, ...) {} + + inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = cimg_verbosity; + if (is_set) mode = value; + return mode; + } + + //! Set current \CImg exception mode. + /** + The way error messages are handled by \CImg can be changed dynamically, using this function. + \param mode Desired exception mode. Possible values are: + - \c 0: Hide library messages (quiet mode). + - \c 1: Print library messages on the console. + - \c 2: Display library messages on a dialog window (default behavior). + - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!). + - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!). + **/ + inline unsigned int& exception_mode(const unsigned int mode) { + return _exception_mode(mode,true); + } + + //! Return current \CImg exception mode. + /** + \note By default, return the value of configuration macro \c cimg_verbosity + **/ + inline unsigned int& exception_mode() { + return _exception_mode(0,false); + } + + inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK", + const char *const button2_label=0, const char *const button3_label=0, + const char *const button4_label=0, const char *const button5_label=0, + const char *const button6_label=0, const bool centering=false); + + inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0); + } + + /*--------------------------------------- + # + # Define the CImgException structures + # + --------------------------------------*/ + //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. + /** + \par Overview + + CImgException is the base class of all exceptions thrown by \CImg. + CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. + These derived classes can be: + + - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. + This is probably one of the most thrown exception by \CImg. + For instance, the following example throws a \c CImgArgumentException: + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float-valued pixels. + img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis. + \endcode + + - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances. + + - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit + the function requirements. For instance, the following example throws a \c CImgInstanceException: + \code + const CImg img; // Define an empty image. + const float value = img.at(0); // Try to read first pixel value (does not exist). + \endcode + + - \b CImgIOException: Thrown when an error occured when trying to load or save image files. + This happens when trying to read files that do not exist or with invalid formats. + For instance, the following example throws a \c CImgIOException: + \code + const CImg img("missing_file.jpg"); // Try to load a file that does not exist. + \endcode + + - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and + when a \CImg function has to display a warning message (see cimg::warn()). + + It is not recommended to throw CImgException instances by yourself, since they are expected to be thrown only by \CImg. + When an error occurs in a library function call, \CImg may display error messages on the screen or on the + standard output, depending on the current \CImg exception mode. + The \CImg exception mode can be get and set by functions cimg::exception_mode() and cimg::exception_mode(unsigned int). + + \par Exceptions handling + + In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. + This may lead the program to break (this is the default behavior), but you can bypass this behavior by handling the exceptions by yourself, + using a usual try { ... } catch () { ... } bloc, as in the following example: + \code + #define "CImg.h" + using namespace cimg_library; + int main() { + cimg::exception_mode(0); // Enable quiet exception mode. + try { + ... // Here, do what you want to stress the CImg library. + } catch (CImgException &e) { // You succeeded: something went wrong! + std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message. + ... // Do what you want now to save the ship! + } + } + \endcode + **/ + struct CImgException : public std::exception { +#define _cimg_exception_err(etype,disp_flag) \ + std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(ap); \ + if (cimg::exception_mode()) { \ + std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ + if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } catch (CImgException&) {} \ + if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ + } + + char _message[16384]; + CImgException() { *_message = 0; } + CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; + + // The CImgInstanceException class is used to throw an exception related + // to an invalid instance encountered in a library function call. + struct CImgInstanceException : public CImgException { + CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; + + // The 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 *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } + }; + + // The 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 *const 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 *const 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 *const format, ...) { _cimg_exception_err("CImgWarningException",false); } + }; + + /*------------------------------------- + # + # Define cimg:: namespace + # + -----------------------------------*/ + //! Contains \a low-level functions and variables of the \CImg Library. + /** + Most of the functions and variables within this namespace are used by the \CImg library for low-level operations. + You may use them to access specific const values or environment variables internally used by \CImg. + \warning Never write using namespace cimg_library::cimg; in your source code. Lot of functions in the + cimg:: namespace have the same names as standard C functions that may be defined in the global namespace ::. + **/ + namespace cimg { + + // Define traits that will be used to determine the best data type to work in CImg functions. + // + template struct type { + static const char* string() { + static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", + "unknown32", "unknown40", "unknown48", "unknown56", + "unknown64", "unknown72", "unknown80", "unknown88", + "unknown96", "unknown104", "unknown112", "unknown120", + "unknown128" }; + return s[(sizeof(T)<17)?sizeof(T):0]; + } + static bool is_float() { return false; } + static bool is_inf(const T) { return false; } + static bool is_nan(const T) { 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 T inf() { return max(); } + static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } + static const char* format() { return "%s"; } + static const char* format(const T val) { static const char *const s = "unknown"; cimg::unused(val); return s; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "bool"; return s; } + static bool is_float() { return false; } + static bool is_inf(const bool) { return false; } + static bool is_nan(const bool) { return false; } + static bool min() { return false; } + static bool max() { return true; } + static bool inf() { return max(); } + static bool is_inf() { return false; } + static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } + static const char* format() { return "%s"; } + static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned char) { return false; } + static bool is_nan(const unsigned char) { return false; } + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)~0U; } + static unsigned char inf() { return max(); } + static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned char val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static char min() { return (char)(-1L<<(8*sizeof(char)-1)); } + static char max() { return (char)~((char)(-1L<<(8*sizeof(char)-1))); } + static char inf() { return max(); } + static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } + static const char* format() { return "%d"; } + static int format(const char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "signed char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const signed char) { return false; } + static bool is_nan(const signed char) { 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 signed char inf() { return max(); } + static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; } + static const char* format() { return "%d"; } + static unsigned int format(const signed char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned short) { return false; } + static bool is_nan(const unsigned short) { return false; } + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)~0U; } + static unsigned short inf() { return max(); } + static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned short val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const short) { return false; } + static bool is_nan(const short) { return false; } + static short min() { return (short)(-1L<<(8*sizeof(short)-1)); } + static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); } + static short inf() { return max(); } + static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } + static const char* format() { return "%d"; } + static int format(const short val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned int) { return false; } + static bool is_nan(const unsigned int) { return false; } + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)~0U; } + static unsigned int inf() { return max(); } + static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const int) { return false; } + static bool is_nan(const int) { return false; } + static int min() { return (int)(-1L<<(8*sizeof(int)-1)); } + static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); } + static int inf() { return max(); } + static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } + static const char* format() { return "%d"; } + static int format(const int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned long"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned long) { return false; } + static bool is_nan(const unsigned long) { return false; } + static unsigned long min() { return 0; } + static unsigned long max() { return (unsigned long)~0UL; } + static unsigned long inf() { return max(); } + static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; } + static const char* format() { return "%lu"; } + static unsigned long format(const unsigned long val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "long"; return s; } + static bool is_float() { return false; } + static bool is_inf(const long) { return false; } + static bool is_nan(const long) { return false; } + static long min() { return (long)(-1L<<(8*sizeof(long)-1)); } + static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); } + static long inf() { return max(); } + static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; } + static const char* format() { return "%ld"; } + static long format(const long val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && val+1==val; +#endif + } + static bool is_nan(const double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val<=0 || val>=0); +#endif + } + static double min() { return -1.7E308; } + static double max() { return 1.7E308; } + static double inf() { return max()*max(); } + static double nan() { static const double val_nan = -std::sqrt(-1.0); return val_nan; } + static double cut(const double val) { return valmax()?max():val; } + static const char* format() { return "%.16g"; } + static double format(const double val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "float"; return s; } + static bool is_float() { return true; } + static bool is_inf(const float val) { return cimg::type::is_inf((double)val); } + static bool is_nan(const float val) { return cimg::type::is_nan((double)val); } + static float min() { return -3.4E38f; } + static float max() { return 3.4E38f; } + static float inf() { return (float)cimg::type::inf(); } + static float nan() { return (float)cimg::type::nan(); } + static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; } + static const char* format() { return "%.16g"; } + static double format(const float val) { return (double)val; } + }; + + template struct superset { typedef T type; }; + template<> struct superset { typedef unsigned char type; }; + template<> struct superset { typedef char type; }; + template<> struct superset { typedef signed char type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + + template struct superset2 { + typedef typename superset::type>::type type; + }; + + template struct superset3 { + typedef typename superset::type>::type type; + }; + + template struct last { typedef t2 type; }; + +#define _cimg_Tt typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_Ttfloat typename cimg::superset2::type +#define _cimg_Ttdouble typename cimg::superset2::type + + // Define variables used internally by CImg. +#if cimg_display==1 + struct X11_info { + volatile unsigned int nb_wins; + pthread_t* event_thread; + CImgDisplay* wins[1024]; + Display* display; + unsigned int nb_bits; + bool is_blue_first; + bool is_shm_enabled; + bool byte_order; +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11_info():nb_wins(0),event_thread(0),display(0), + nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + }; +#if defined(cimg_module) + X11_info& X11_attr(); +#elif defined(cimg_main) + X11_info& X11_attr() { static X11_info val; return val; } +#else + inline X11_info& X11_attr() { static X11_info val; return val; } +#endif + +#elif cimg_display==2 + struct Win32_info { + HANDLE wait_event; + Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } + }; +#if defined(cimg_module) + Win32_info& Win32_attr(); +#elif defined(cimg_main) + Win32_info& Win32_attr() { static Win32_info val; return val; } +#else + inline Win32_info& Win32_attr() { static Win32_info val; return val; } +#endif +#endif + +#if defined(cimg_use_magick) + static struct Magick_info { + Magick_info() { + Magick::InitializeMagick(""); + } + } _Magick_info; +#endif + +#if cimg_display==1 + // Define 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 + // Define 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; + +#else + // Define random keycodes when no display is available. + // (should rarely be used then!). + const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent). + const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent). + const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent). + const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent). + const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent). + const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent). + const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent). + const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent). + const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent). + const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent). + const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent). + const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent). + const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent). + const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent). + const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent). + const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent). + const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent). + const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent). + const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent). + const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent). + const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent). + const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent). + const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent). + const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent). + const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent). + const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent). + const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent). + const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent). + const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent). + const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent). + const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent). + const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent). + const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent). + const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent). + const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent). + const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent). + const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent). + const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent). + const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent). + const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent). + const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent). + const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent). + const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent). + const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent). + const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent). + const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent). + const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent). + const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent). + const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent). + const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent). + const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent). + const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent). + const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent). + const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent). + const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent). + const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent). + const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent). + const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent). + const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent). + const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent). + const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent). + const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent). + const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent). + const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent). + const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent). + const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent). + const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent). + const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent). + const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent). + const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent). + const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent). + const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent). + const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent). + const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent). + const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent). + const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent). + const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent). + const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent). + const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent). + const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent). + const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent). + const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent). + const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent). + const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent). + const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent). + const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent). + const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent). + const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent). +#endif + + const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI + + // Define a 10x13 font (small size). + 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 + }; + + // Define a 12x24 font (normal size). + 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 }; + + // Define a 16x32 font (large size). + 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 }; + + // Define a 29x57 font (extra large size). + 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 }; + + // Define a 40x38 'danger' color logo (used by cimg::dialog()). + 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}; + + //! Get/set default output stream for the \CImg library messages. + /** + \param file Desired output stream. Set to \c 0 to get the currently used output stream only. + \return Currently used output stream. + **/ + inline std::FILE* output(std::FILE *file) { + static std::FILE *res = stderr; + if (file) res = file; + return res; + } + + //! Display a warning message on the default output stream. + /** + \param format C-string containing the format of the message, as with std::printf(). + \note If configuration macro \c cimg_strict_warnings is set, this function throws a \c CImgWarningException instead. + \warning As the first argument is a format string, it is highly recommended to write + \code + cimg::warn("%s",warning_message); + \endcode + instead of + \code + cimg::warn(warning_message); + \endcode + if \c warning_message can be arbitrary, to prevent nasty memory access. + **/ + inline void warn(const char *const format, ...) { + if (cimg::exception_mode()>=1) { + char message[16384] = { 0 }; + std::va_list ap; + va_start(ap,format); + cimg_vsnprintf(message,sizeof(message),format,ap); + va_end(ap); +#ifdef cimg_strict_warnings + throw CImgWarningException(message); +#else + std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s",cimg::t_red,cimg::t_normal,message); +#endif + } + } + + // Execute an external system command. + /** + \param command C-string containing the command line to execute. + \param module_name Module name. + \return Status value of the executed command, whose meaning is OS-dependent. + \note This function is similar to std::system() + but it does not open an extra console windows + on Windows-based systems. + **/ + inline int system(const char *const command, const char *const module_name=0) { +#ifdef cimg_no_system_calls + return -1; +#else +#if cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFO si; + std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); + std::memset(&si,0,sizeof(STARTUPINFO)); + GetStartupInfo(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW; + 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 std::system(command); + return module_name?0:1; +#endif + } + + //! Return a reference to a temporary variable of type T. + template + inline T& temporary(const T&) { + static T temp; + return temp; + } + + //! Exchange values of variables \c a and \c b. + template + inline void swap(T& a, T& b) { T t = a; a = b; b = t; } + + //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + + //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7, T8& a8, T8& b8) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); + } + + //! Return the endianness of the current architecture. + /** + \return \c false for Little Endian or \c true for Big Endian. + **/ + inline bool endianness() { + const int x = 1; + return ((unsigned char*)&x)[0]?false:true; + } + + //! Reverse endianness of all elements in a memory buffer. + /** + \param[in,out] buffer Memory buffer whose endianness must be reversed. + \param size Number of buffer elements to reverse. + **/ + template + inline void invert_endianness(T* const buffer, const unsigned long 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)); + } + } + } + } + + //! Reverse endianness of a single variable. + /** + \param[in,out] a Variable to reverse. + \return Reference to reversed variable. + **/ + template + inline T& invert_endianness(T& a) { + invert_endianness(&a,1); + return a; + } + + // Conversion functions to get more precision when trying to store unsigned ints values as floats. + inline unsigned int float2uint(const float f) { + int tmp = 0; + std::memcpy(&tmp,&f,sizeof(float)); + if (tmp>=0) return (unsigned int)f; + unsigned int u; + std::memcpy(&u,&f,sizeof(float)); // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. + return ((u)<<1)>>1; // set sign bit to 0. + } + + inline float uint2float(const unsigned int u) { + if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287). + float f; + const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1. + std::memcpy(&f,&v,sizeof(float)); // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. + return f; + } + + //! Return the value of a system timer, with a millisecond precision. + /** + \note The timer does not necessarily starts from \c 0. + **/ + 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 + 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 + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline unsigned long tictoc(const bool is_tic) { + static unsigned long t0 = 0; + const unsigned long t = cimg::time(); + if (is_tic) return (t0 = t); + const unsigned long dt = t>=t0?(t - t0):cimg::type::max(); + const unsigned int + edays = (unsigned int)(dt/86400000.0), + ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0), + emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0), + esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0), + ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0); + if (!edays && !ehours && !emin && !esec) + std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u ms%s\n",cimg::t_red,ems,cimg::t_normal); + else { + if (!edays && !ehours && !emin) + std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u sec %u ms%s\n",cimg::t_red,esec,ems,cimg::t_normal); + else { + if (!edays && !ehours) + std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u min %u sec %u ms%s\n",cimg::t_red,emin,esec,ems,cimg::t_normal); + else{ + if (!edays) + std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u hours %u min %u sec %u ms%s\n",cimg::t_red,ehours,emin,esec,ems,cimg::t_normal); + else{ + std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u days %u hours %u min %u sec %u ms%s\n",cimg::t_red,edays,ehours,emin,esec,ems,cimg::t_normal); + } + } + } + } + return t; + } + + //! Start tic/toc timer for time measurement between code instructions. + /** + \return Current value of the timer (same value as time()). + **/ + inline unsigned long tic() { + return cimg::tictoc(true); + } + + //! End tic/toc timer and displays elapsed time from last call to tic(). + /** + \return Time elapsed (in ms) since last call to tic(). + **/ + inline unsigned long toc() { + return cimg::tictoc(false); + } + + //! Sleep for a given numbers of milliseconds. + /** + \param milliseconds Number of milliseconds to wait for. + \note This function frees the CPU ressources during the sleeping time. + It can 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 _wait(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 given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \return Number of milliseconds elapsed since the last call to wait(). + \note Same as sleep() with a waiting time computed with regard to the last call + of wait(). It may be used to temporize your program properly, without wasting CPU time. + **/ + inline unsigned int wait(const unsigned int milliseconds) { + static unsigned long timer = 0; + if (!timer) timer = cimg::time(); + return _wait(milliseconds,timer); + } + + // Random number generators. + // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set. + // Use it for instance when you have to deal with concurrent threads trying to call std::srand() + // at the same time! +#ifdef cimg_use_rng + + // Use a custom RNG. + inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) { + static unsigned long next = 1; + if (set_seed) next = (unsigned long)seed; + next = next*1103515245 + 12345; + return (unsigned int)((next>>16)&0x7FFF); + } + + inline void srand() { + const unsigned int t = (unsigned int)cimg::time(); +#if cimg_OS==1 + cimg::_rand(t+(unsigned int)getpid(),true); +#elif cimg_OS==2 + cimg::_rand(t+(unsigned int)_getpid(),true); +#else + cimg::_rand(t,true); +#endif + } + + inline void srand(const unsigned int seed) { + _rand(seed,true); + } + + inline double rand() { + return cimg::_rand()/32767.; + } + +#else + + // Use the system RNG. + inline void srand() { + const unsigned int t = (unsigned int)cimg::time(); +#if cimg_OS==1 + std::srand(t+(unsigned int)getpid()); +#elif cimg_OS==2 + std::srand(t+(unsigned int)_getpid()); +#else + std::srand(t); +#endif + } + + inline void srand(const unsigned int seed) { + std::srand(seed); + } + + //! Return a random variable between [0,1] with respect to an uniform distribution. + /** + **/ + inline double rand() { + return (double)std::rand()/RAND_MAX; + } +#endif + + //! 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*std::sqrt((-2*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) 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; + } + + //! Bitwise-rotate value on the left. + template + inline T rol(const T a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3)-n))):a; + } + + inline float rol(const float a, const unsigned int n=1) { + return (float)rol((int)a,n); + } + + inline double rol(const double a, const unsigned int n=1) { + return (double)rol((long)a,n); + } + + //! Bitwise-rotate value on the right. + template + inline T ror(const T a, const unsigned int n=1) { + return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a; + } + + inline float ror(const float a, const unsigned int n=1) { + return (float)ror((int)a,n); + } + + inline double ror(const double a, const unsigned int n=1) { + return (double)ror((long)a,n); + } + + //! Return absolute value of a value. + template + inline T abs(const T a) { + return a>=0?a:-a; + } + inline bool abs(const bool a) { + return a; + } + inline unsigned char abs(const unsigned char a) { + return a; + } + inline unsigned short abs(const unsigned short a) { + return a; + } + inline unsigned int abs(const unsigned int a) { + return a; + } + inline unsigned long abs(const unsigned long a) { + return a; + } + inline double abs(const double a) { + return std::fabs(a); + } + inline float abs(const float a) { + return (float)std::fabs((double)a); + } + inline int abs(const int a) { + return std::abs(a); + } + + //! Return square of a value. + template + inline T sqr(const T val) { + return val*val; + } + + //! Return 1 + log_10(x) of a value \c x. + inline int xln(const int x) { + return x>0?(int)(1+std::log10((double)x)):1; + } + + //! Return the minimum between two values. + template + inline typename cimg::superset::type min(const t1& a, const t2& b) { + typedef typename cimg::superset::type t1t2; + return (t1t2)(a<=b?a:b); + } + + //! Return the minimum between three values. + template + inline typename cimg::superset2::type min(const t1& a, const t2& b, const t3& c) { + typedef typename cimg::superset2::type t1t2t3; + return (t1t2t3)cimg::min(cimg::min(a,b),c); + } + + //! Return the minimum between four values. + template + inline typename cimg::superset3::type min(const t1& a, const t2& b, const t3& c, const t4& d) { + typedef typename cimg::superset3::type t1t2t3t4; + return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d); + } + + //! Return the maximum between two values. + template + inline typename cimg::superset::type max(const t1& a, const t2& b) { + typedef typename cimg::superset::type t1t2; + return (t1t2)(a>=b?a:b); + } + + //! Return the maximum between three values. + template + inline typename cimg::superset2::type max(const t1& a, const t2& b, const t3& c) { + typedef typename cimg::superset2::type t1t2t3; + return (t1t2t3)cimg::max(cimg::max(a,b),c); + } + + //! Return the maximum between four values. + template + inline typename cimg::superset3::type max(const t1& a, const t2& b, const t3& c, const t4& d) { + typedef typename cimg::superset3::type t1t2t3t4; + return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d); + } + + //! Return the sign of a value. + template + inline T sign(const T x) { + return (x<0)?(T)(-1):(x==0?(T)0:(T)1); + } + + //! Return the nearest power of 2 higher than given value. + template + inline unsigned long nearest_pow2(const T x) { + unsigned long i = 1; + while (x>i) i<<=1; + return i; + } + + //! Return the sinc of a given value. + inline double sinc(const double x) { + return x?std::sin(x)/x:1; + } + + //! Return the modulo of a value. + /** + \param x Input value. + \param m Modulo value. + \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type. + **/ + template + inline T mod(const T& x, const T& m) { + const double dx = (double)x, dm = (double)m; + return (T)(dx - dm * 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 min-mod of two values. + /** + \note minmod(\p a,\p b) is defined to be: + - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. + - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. + **/ + template + inline T minmod(const T a, const T b) { + return a*b<=0?0:(a>0?(a + inline T round(const T x, const double y=1, const int rounding_type=0) { + if (y<=0) return x; + const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor; + return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx))); + } + + 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*std::sqrt(1.0+tmp*tmp); } + else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0+tmp*tmp); } + } + + //! Convert ascii character to lower case. + inline char uncase(const char x) { + return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); + } + + //! Convert C-string to lower case. + inline void uncase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uncase(*ptr); + } + + //! Read value in a C-string. + /** + \param str C-string containing the float value to read. + \return Read value. + \note Same as std::atof() extended to manage the retrieval of fractions from C-strings, as in "1/2". + **/ + inline double atof(const char *const str) { + double x = 0, y = 1; + if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; } + } + + //! Compare the first \p l characters of two C-strings, ignoring the case. + /** + \param str1 C-string. + \param str2 C-string. + \param l Number of characters to compare. + \return \c 0 if the two strings are equal, something else otherwise. + \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). + **/ + inline int strncasecmp(const char *const str1, const char *const str2, const int l) { + if (!l) return 0; + if (!str1) return str2?-1:0; + const char *nstr1 = str1, *nstr2 = str2; + int k, diff = 0; for (k = 0; kp && str[q]==delimiter; ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str+p,n); str[n] = 0; return true; } + return false; + } + + //! Replace escape sequences in C-strings by their binary ascii values. + /** + \param[in,out] str C-string to work with (modified at output). + **/ + inline void strunescape(char *const str) { +#define cimg_strunescape(ci,co) case ci: *nd = co; ++ns; break; + static unsigned int val = 0; + for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) { + cimg_strunescape('n','\n'); + cimg_strunescape('t','\t'); + cimg_strunescape('v','\v'); + cimg_strunescape('b','\b'); + cimg_strunescape('r','\r'); + cimg_strunescape('f','\f'); + cimg_strunescape('a','\a'); + cimg_strunescape('\\','\\'); + cimg_strunescape('\?','\?'); + cimg_strunescape('\'','\''); + cimg_strunescape('\"','\"'); + case 0 : *nd = 0; break; + case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : + std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns; + *nd = val; break; + case 'x': + std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; + *nd = val; break; + default : *nd = *(ns++); + } else *nd = *(ns++); + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const unsigned long size) { + static char res[256] = { 0 }; + if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":""); + else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kio",nsize); } + else if (size<1024*1024*1024LU) { const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mio",nsize); } + else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gio",nsize); } + return res; + } + + //! Return the basename of a filename. + inline const char* basename(const char *const str) { + const char *p = 0; + for (const char *np = str; np>=str && (p=np); np = std::strchr(np,cimg_file_separator)+1) {} + return p; + } + + // Return a random filename. + inline const char* filenamerand() { + static char randomid[9] = { 0,0,0,0,0,0,0,0,0 }; + cimg::srand(); + for (unsigned int k = 0; k<8; ++k) { + const int v = (int)std::rand()%3; + randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26)))); + } + return randomid; + } + + // Convert filename as a Windows-style filename (short path name). + inline void winformat_string(char *const str) { + if (str && *str) { +#if cimg_OS==2 + char *const nstr = new char[MAX_PATH]; + if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); +#endif + } + } + + //! Open a file. + /** + \param path Path of the filename to open. + \param mode C-string describing the opening mode. + \return Opened file. + \note Same as std::fopen() but throw a \c CImgIOException when + the specified file cannot be opened, instead of returning \c 0. + **/ + inline std::FILE *fopen(const char *const path, const char *const mode) { + if (!path) + throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); + if (!mode) + throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", + path); + std::FILE *res = 0; + if (*path=='-' && (!path[1] || path[1]=='.')) { + res = (*mode=='r')?stdin:stdout; +#if cimg_OS==2 + if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode. + if (_setmode(_fileno(res),0x8000)==-1) res = 0; + } +#endif + } else res = std::fopen(path,mode); + if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.", + path,mode); + return res; + } + + //! Close a file. + /** + \param file File to close. + \return \c 0 if file has been closed properly, something else otherwise. + \note Same as std::fclose() but display a warning message if + the file has not been closed properly. + **/ + inline int fclose(std::FILE *file) { + if (!file) warn("cimg::fclose(): Specified file is (null)."); + if (!file || file==stdin || file==stdout) return 0; + const int errn = std::fclose(file); + if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", + errn); + return errn; + } + + //! Get/set path to store temporary files. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path where temporary files can be saved. + **/ + 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_snprintf(st_path,1024,"%s",p); \ + cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",st_path,cimg_file_separator,filetmp); \ + if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ + } + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + char tmp[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file = 0; + cimg_snprintf(filetmp,sizeof(filetmp),"%s.tmp",cimg::filenamerand()); + char *tmpPath = std::getenv("TMP"); + if (!tmpPath) { tmpPath = std::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; + std::strncpy(tmp,filetmp,sizeof(tmp)-1); + if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } + } + if (!path_found) + throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); + } + return st_path; + } + + //! Get/set path to the Program Files/ directory (Windows only). + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the program files. + **/ +#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) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[MAX_PATH]; + 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 *const pfPath = std::getenv("PROGRAMFILES"); + if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1); + else std::strcpy(st_path,"C:\\PROGRA~1"); + } +#else + std::strcpy(st_path,"C:\\PROGRA~1"); +#endif + } + return st_path; + } +#endif + + //! Get/set path to the ImageMagick's \c convert binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c convert binary. + **/ + inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + const char *const pf_path = programfiles_path(); + if (!path_found) { + std::strcpy(st_path,".\\convert.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"convert.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./convert"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"convert"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the GraphicsMagick's \c gm binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gm binary. + **/ + inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + const char *const pf_path = programfiles_path(); + if (!path_found) { + std::strcpy(st_path,".\\gm.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gm.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./gm"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gm"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the XMedcon's \c medcon binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c medcon binary. + **/ + inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + const char *const pf_path = programfiles_path(); + if (!path_found) { + std::strcpy(st_path,".\\medcon.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"medcon.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./medcon"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"medcon"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the FFMPEG's \c ffmpeg binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c ffmpeg binary. + **/ + inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\ffmpeg.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"ffmpeg.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./ffmpeg"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"ffmpeg"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the \c gzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gzip binary. + **/ + inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\gzip.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gzip.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./gzip"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gzip"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the \c gzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gunzip binary. + **/ + inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\gunzip.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gunzip.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./gunzip"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"gunzip"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the \c dcraw binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c dcraw binary. + **/ + inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\dcraw.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"dcraw.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./dcraw"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"dcraw"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the \c wget binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c wget binary. + **/ + inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\wget.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"wget.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./wget"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"wget"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Get/set path to the \c curl binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c curl binary. + **/ + inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + std::memset(st_path,0,1024); + std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + std::memset(st_path,0,1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + std::strcpy(st_path,".\\curl.exe"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"curl.exe"); +#else + if (!path_found) { + std::strcpy(st_path,"./curl"); + if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_path,"curl"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Split filename into two C-strings \c body and \c extension. + inline const char *split_filename(const char *const filename, char *const body=0) { + if (!filename) { if (body) *body = 0; return 0; } + const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {} + if (p==filename) { + if (body) std::strcpy(body,filename); + return filename + std::strlen(filename); + } + const unsigned int l = (unsigned int)(p - filename - 1); + if (body) { std::memcpy(body,filename,l); body[l] = 0; } + return p; + } + + //! Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const str) { + if (!filename) { if (str) *str = 0; return 0; } + char format[1024] = { 0 }, body[1024] = { 0 }; + const char *const ext = cimg::split_filename(filename,body); + if (n>0) cimg_snprintf(format,sizeof(format),"%s_%%.%ud.%s",body,n,ext); + else cimg_snprintf(format,sizeof(format),"%s_%%d.%s",body,ext); + std::sprintf(str,format,number); + return str; + } + + //! Try to guess format from an image file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + \return C-string containing the guessed file format, or \c 0 if nothing has been guessed. + **/ + inline const char *file_type(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::file_type(): Specified filename is (null)."); + static const char + *const _pnm = "pnm", + *const _pfm = "pfm", + *const _bmp = "bmp", + *const _gif = "gif", + *const _jpg = "jpg", + *const _off = "off", + *const _pan = "pan", + *const _png = "png", + *const _tif = "tif", + *const _inr = "inr", + *const _dcm = "dcm"; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + const char *f_type = 0, *head; + char header[2048] = { 0 }, item[1024] = { 0 }; + const unsigned char *const uheader = (unsigned char*)header; + int err; char cerr; + const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile); // Read first 2048 bytes. + if (!file) cimg::fclose(nfile); + + if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format. + else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format. + else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format. + else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format. + else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // Check for JPEG format. + else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // Check for BMP format. + else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format. + (header[4]=='7' || header[4]=='9')) f_type = _gif; + else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // Check for PNG format. + uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png; + else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // Check for TIFF format. + else { // Check for PNM or PFM format. + head = header; + while (headstd::fread() but may display warning message if all elements could not be read. + **/ + template + inline int fread(T *const ptr, const unsigned long nmemb, std::FILE *stream) { + if (!ptr || nmemb<=0 || !stream) + throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",stream,ptr); + + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned long to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; + do { + l_to_read = (to_read*sizeof(T))0); + if (to_read>0) + warn("cimg::fread(): Only %u/%u elements could be read from file.", + al_read,nmemb); + return al_read; + } + + //! Write data to file. + /** + \param ptr Pointer to memory buffer containing the binary data to write on file. + \param nmemb Number of elements to write. + \param[out] stream File to write data on. + \return Number of written elements. + \note Similar to std::fwrite but may display warning messages if all elements could not be written. + **/ + template + inline int fwrite(const T *ptr, const unsigned long nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",ptr,stream); + if (nmemb<=0) return 0; + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned long to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; + do { + l_to_write = (to_write*sizeof(T))0); + if (to_write>0) + warn("cimg::fwrite(): Only %u/%u elements could be written in file.", + al_write,nmemb); + return al_write; + } + + //! Create an empty file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + **/ + inline void fempty(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::file_type(): Specified filename is (null)."); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!file) cimg::fclose(nfile); + } + + //! Load file from network as a local temporary file. + /** + \param filename Filename, as a C-string. + \param[out] filename_local C-string containing the path to a local copy of \c filename. + \return Value of \c filename_local. + \note Use external binaries \c wget or \c curl to perform. You must have one of these tools installed + to be able to use this function. + **/ + inline char *load_network_external(const char *const filename, char *const filename_local) { + if (!filename) + throw CImgArgumentException("cimg::load_network_external(): Specified filename is (null)."); + if (!filename_local) + throw CImgArgumentException("cimg::load_network_external(): Specified destination string is (null)."); + const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext; + char command[1024] = { 0 }; + std::FILE *file = 0; + *filename_local = 0; + do { + cimg_snprintf(filename_local,512,"%s%c%s%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file); + } while (file); + + // Try with 'curl' first. + cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local,filename); + cimg::system(command); + if (!(file = std::fopen(filename_local,"rb"))) { + + // Try with 'wget' else. + cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local,filename); + cimg::system(command); + if (!(file = std::fopen(filename_local,"rb"))) + throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external tools 'wget' or 'curl'.",filename); + cimg::fclose(file); + + // Try gunzip it. + cimg_snprintf(command,sizeof(command),"%s.gz",filename_local); + std::rename(filename_local,command); + cimg_snprintf(command,sizeof(command),"%s --quiet \"%s.gz\"", + gunzip_path(),filename_local); + cimg::system(command); + file = std::fopen(filename_local,"rb"); + if (!file) { + cimg_snprintf(command,sizeof(command),"%s.gz",filename_local); + std::rename(command,filename_local); + file = std::fopen(filename_local,"rb"); + } + } + std::fseek(file,0,SEEK_END); // Check if file size is 0. + if (std::ftell(file)<=0) + throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external commands 'wget' or 'curl'.",filename); + cimg::fclose(file); + return filename_local; + } + + //! Return options specified on the command line. + inline const char* option(const char *const name, const int argc, const char *const *const argv, + const char *const defaut, const char *const usage, const bool reset_static) { + static bool first = true, visu = false; + if (reset_static) { first = true; return 0; } + const char *res = 0; + if (first) { + first = false; + visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0; + } + if (!name && visu) { + if (usage) { + std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + std::fprintf(cimg::output(),": %s",usage); + std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__); + } + if (defaut) std::fprintf(cimg::output(),"%s\n",defaut); + } + if (name) { + if (argc>0) { + int k = 0; + while (k Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), + cimg::t_normal,cimg::t_green, + cimg_OS, + cimg::t_normal); + + std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n", + cimg::t_bold, + cimg::endianness()?"Big":"Little", + cimg::t_normal); + + std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n", + cimg::t_bold, + cimg_verbosity==0?"Quiet":(cimg_verbosity==1?"Console":(cimg_verbosity==2?"Dialog":(cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings"))), + cimg::t_normal,cimg::t_green, + cimg_verbosity, + cimg::t_normal); + + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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":"Unknown", + cimg::t_normal,cimg::t_green, + cimg_display, + cimg::t_normal); + +#if cimg_display==1 + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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 + std::fprintf(cimg::output()," > 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); + std::fprintf(cimg::output()," > 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); + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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); + + std::fprintf(cimg::output()," > 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_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path()); + std::fprintf(cimg::output()," > Path of ImageMagick: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path()); + std::fprintf(cimg::output()," > Path of GraphicsMagick: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path()); + std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path()); + std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + std::fprintf(cimg::output(),"\n"); + } + + // Declare LAPACK function signatures if LAPACK support is enabled. +#ifdef cimg_use_lapack + template + inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { + dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { + sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + template + inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { + dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { + sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + template + inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, + T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { + dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, + float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { + sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + template + inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { + int one = 1; + dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { + int one = 1; + sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + template + inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { + dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { + ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + template + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA, + T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){ + dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA, + float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){ + sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + +#endif + + // End of the 'cimg' namespace + } + + /*------------------------------------------------ + # + # + # Definition of mathematical operators and + # external functions. + # + # + -------------------------------------------------*/ + +#define _cimg_create_ext_operators(typ) \ + template \ + inline CImg::type> operator+(const typ val, const CImg& img) { \ + return img + val; \ + } \ + template \ + inline CImg::type> operator-(const typ val, const CImg& img) { \ + typedef typename cimg::superset::type Tt; \ + return CImg(img._width,img._height,img._depth,img._spectrum,val)-=img; \ + } \ + template \ + inline CImg::type> operator*(const typ val, const CImg& img) { \ + return img*val; \ + } \ + template \ + inline CImg::type> operator/(const typ val, const CImg& img) { \ + return val*img.get_invert(); \ + } \ + template \ + inline CImg::type> operator&(const typ val, const CImg& img) { \ + return img & val; \ + } \ + template \ + inline CImg::type> operator|(const typ val, const CImg& img) { \ + return img | val; \ + } \ + template \ + inline CImg::type> operator^(const typ val, const CImg& img) { \ + return img ^ val; \ + } \ + template \ + inline bool operator==(const typ val, const CImg& img) { \ + return img == val; \ + } \ + template \ + inline bool operator!=(const typ val, const CImg& img) { \ + return img != val; \ + } + + _cimg_create_ext_operators(bool) + _cimg_create_ext_operators(unsigned char) + _cimg_create_ext_operators(char) + _cimg_create_ext_operators(signed char) + _cimg_create_ext_operators(unsigned short) + _cimg_create_ext_operators(short) + _cimg_create_ext_operators(unsigned int) + _cimg_create_ext_operators(int) + _cimg_create_ext_operators(unsigned long) + _cimg_create_ext_operators(long) + _cimg_create_ext_operators(float) + _cimg_create_ext_operators(double) + + template + inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { + return img + expression; + } + + template + inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { + return CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum,expression,true)-=img; + } + + template + inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg& img) { + return img*expression; + } + + template + inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg& img) { + return expression*img.get_invert(); + } + + template + inline CImg operator&(const char *const expression, const CImg& img) { + return img & expression; + } + + template + inline CImg operator|(const char *const expression, const CImg& img) { + return img | expression; + } + + template + inline CImg operator^(const char *const expression, const CImg& img) { + return img ^ expression; + } + + template + inline bool operator==(const char *const expression, const CImg& img) { + return img == expression; + } + + template + inline bool operator!=(const char *const expression, const CImg& img) { + return img != expression; + } + + template + inline CImg<_cimg_Tfloat> sqr(const CImg& instance) { + return instance.get_sqr(); + } + + template + inline CImg<_cimg_Tfloat> sqrt(const CImg& instance) { + return instance.get_sqrt(); + } + + template + inline CImg<_cimg_Tfloat> exp(const CImg& instance) { + return instance.get_exp(); + } + + template + inline CImg<_cimg_Tfloat> log(const CImg& instance) { + return instance.get_log(); + } + + template + inline CImg<_cimg_Tfloat> log2(const CImg& instance) { + return instance.get_log2(); + } + + template + inline CImg<_cimg_Tfloat> log10(const CImg& instance) { + return instance.get_log10(); + } + + template + inline CImg<_cimg_Tfloat> abs(const CImg& instance) { + return instance.get_abs(); + } + + template + inline CImg<_cimg_Tfloat> sign(const CImg& instance) { + return instance.get_sign(); + } + + template + inline CImg<_cimg_Tfloat> cos(const CImg& instance) { + return instance.get_cos(); + } + + template + inline CImg<_cimg_Tfloat> sin(const CImg& instance) { + return instance.get_sin(); + } + + template + inline CImg<_cimg_Tfloat> sinc(const CImg& instance) { + return instance.get_sinc(); + } + + template + inline CImg<_cimg_Tfloat> tan(const CImg& instance) { + return instance.get_tan(); + } + + template + inline CImg<_cimg_Tfloat> acos(const CImg& instance) { + return instance.get_acos(); + } + + template + inline CImg<_cimg_Tfloat> asin(const CImg& instance) { + return instance.get_asin(); + } + + template + inline CImg<_cimg_Tfloat> atan(const CImg& instance) { + return instance.get_atan(); + } + + template + inline CImg<_cimg_Tfloat> cosh(const CImg& instance) { + return instance.get_cosh(); + } + + template + inline CImg<_cimg_Tfloat> sinh(const CImg& instance) { + return instance.get_sinh(); + } + + template + inline CImg<_cimg_Tfloat> tanh(const CImg& instance) { + return instance.get_tanh(); + } + + template + inline CImg transpose(const CImg& instance) { + return instance.get_transpose(); + } + + template + inline CImg<_cimg_Tfloat> invert(const CImg& instance) { + return instance.get_invert(); + } + + template + inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance) { + return instance.get_pseudoinvert(); + } + + /*----------------------------------- + # + # Define the CImgDisplay structure + # + ----------------------------------*/ + //! Allow to create windows, display images on them and manage user events (keyboard, mouse and windows events). + /** + CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window (X11, for Unix-based systems) + or \b GDI32 (for Windows-based systems). + If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter a minimal mode + where warning messages will be outputed each time the program is trying to call one of the CImgDisplay method. + + The configuration variable \c cimg_display tells about the graphic library used. + It is set automatically by \CImg when one of these graphic libraries has been detected. + But, you can override its value if necessary. Valid choices are: + - 0: Disable display capabilities. + - 1: Use \b X-Window (X11) library. + - 2: Use \b GDI32 library. + + Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay. + **/ + struct CImgDisplay { + unsigned long _timer, _fps_frames, _fps_timer; + unsigned int _width, _height, _normalization; + float _fps_fps, _min, _max; + bool _is_fullscreen; + char *_title; + volatile unsigned int _window_width, _window_height, _button, _keys[128], _released_keys[128]; + volatile int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; + volatile bool _is_closed, _is_resized, _is_moved, _is_event, + _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; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- + +#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 + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + \note If the associated window is visible on the screen, it is closed by the call to the destructor. + **/ + ~CImgDisplay() { + assign(); + } + + //! Construct an empty display. + /** + \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until + display of valid data is performed. + \par Example + \code + CImgDisplay disp; // Does actually nothing. + ... + disp.display(img); // Construct new window and display image in it. + \endcode + **/ + CImgDisplay(): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(); + } + + //! Construct a display with specified dimensions. + /** \param width Window width. + \param height Window height. + \param title Window title. + \param normalization Normalization type (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note A black background is initially displayed on the associated window. + **/ + CImgDisplay(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(width,height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image. + /** \param img Image used as a model to create the window. + \param title Window title. + \param normalization Normalization type (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note The pixels of the input image are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(img,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list. + /** \param list The images list to display. + \param title Window title. + \param normalization Normalization type (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note All images of the list, appended along the X-axis, are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(list,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of an existing one. + /** + \param disp Display instance to copy. + \note The pixel buffer of the input window is initially displayed on the associated window. + **/ + CImgDisplay(const CImgDisplay& disp): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(disp); + } + +#if cimg_display==0 + + static void _no_display_exception() { + throw CImgDisplayException("CImgDisplay(): No display available."); + } + + //! Destructor - Empty constructor \inplace. + /** + \note Replace the current instance by an empty display. + **/ + CImgDisplay& assign() { + return flush(); + } + + //! Construct a display with specified dimensions \inplace. + /** + **/ + CImgDisplay& assign(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + cimg::unused(width,height,title,normalization,is_fullscreen,is_closed); + _no_display_exception(); + return assign(); + } + + //! Construct a display from an image \inplace. + /** + **/ + template + CImgDisplay& assign(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list \inplace. + /** + **/ + template + CImgDisplay& assign(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of another one \inplace. + /** + **/ + CImgDisplay& assign(const CImgDisplay &disp) { + _no_display_exception(); + return assign(disp._width,disp._height); + } + +#endif + + //! Return a reference to an empty display. + /** + \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&) + must have a default value. + \par Example + \code + void foo(CImgDisplay& disp=CImgDisplay::empty()); + \endcode + **/ + static CImgDisplay& empty() { + static CImgDisplay _empty; + return _empty.assign(); + } + +#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, const unsigned int dz, + const int dmin, const int dmax,const bool return_y) { + const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0); + unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1; + const unsigned int + sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(), + mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin, + mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin, + Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax, + Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax; + if (nwMw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; } + if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; } + if (nwdisp = img is equivalent to disp.display(img). + **/ + template + CImgDisplay& operator=(const CImg& img) { + return display(img); + } + + //! Display list of images on associated window. + /** + \note disp = list is equivalent to disp.display(list). + **/ + template + CImgDisplay& operator=(const CImgList& list) { + return display(list); + } + + //! Construct a display as a copy of another one \inplace. + /** + \note Equivalent to assign(const CImgDisplay&). + **/ + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return \c false if display is empty, \c true otherwise. + /** + \note if (disp) { ... } is equivalent to if (!disp.is_empty()) { ... }. + **/ + operator bool() const { + return !is_empty(); + } + + //@} + //------------------------------------------ + // + //! \name Instance Checking + //@{ + //------------------------------------------ + + //! Return \c true if display is empty, \c false otherwise. + /** + **/ + bool is_empty() const { + return !(_width && _height); + } + + //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise. + /** + \note + - When a user physically closes the associated window, the display is set to closed. + - A closed display is not destroyed. Its associated window can be show again on the screen using show(). + **/ + bool is_closed() const { + return _is_closed; + } + + //! Return \c true if associated window has been resized on the screen, \c false otherwise. + /** + **/ + bool is_resized() const { + return _is_resized; + } + + //! Return \c true if associated window has been moved on the screen, \c false otherwise. + /** + **/ + bool is_moved() const { + return _is_moved; + } + + //! Return \c true if any event has occured on the associated window, \c false otherwise. + /** + **/ + bool is_event() const { + return _is_event; + } + + //! Return \c true if current display is in fullscreen mode, \c false otherwise. + /** + **/ + bool is_fullscreen() const { + return _is_fullscreen; + } + + //! Return \c true if any key is being pressed on the associated window, \c false otherwise. + /** + \note The methods below do the same only for specific keys. + **/ + bool is_key() const { + return _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; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode Keycode to test. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())'. + disp.wait(); + } + \endcode + **/ + bool is_key(const unsigned int keycode) const { +#define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k; + _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3); + _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7); + _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11); + _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2); + _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6); + _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0); + _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME); + _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W); + _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y); + _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P); + _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN); + _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D); + _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J); + _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER); + _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C); + _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M); + _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT); + _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR); + _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT); + _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT); + _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2); + _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5); + _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8); + _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB); + _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV); + return false; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode C-string containing the keycode label of the key to test. + \note Use it when the key you want to test can be dynamically set by the user. + \par Example + \code + CImgDisplay disp(400,400); + const char *const keycode = "TAB"; + while (!disp.is_closed()) { + if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())'. + disp.wait(); + } + \endcode + **/ + bool is_key(const char *const keycode) const { +#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k; + _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3); + _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7); + _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11); + _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2); + _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6); + _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0); + _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME); + _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W); + _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y); + _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P); + _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN); + _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D); + _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J); + _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER); + _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C); + _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M); + _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT); + _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR); + _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT); + _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT); + _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2); + _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5); + _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8); + _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB); + _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV); + return false; + } + + //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise. + /** + \param keycodes_sequence Buffer of keycodes to test. + \param length Number of keys in the \c keycodes_sequence buffer. + \param remove_sequence Tells if the key sequence must be removed from the key history, if found. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD }; + while (!disp.is_closed()) { + if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event. + disp.wait(); + } + \endcode + **/ + bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, const bool remove_sequence=false) { + if (keycodes_sequence && length) { + const unsigned int + *const ps_end = keycodes_sequence + length - 1, + *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length, + k = *ps_end; + for (unsigned int *pk = (unsigned int*)_keys; pk[0,255]. If the range of values of the data to display + is different, a normalization may be required for displaying the data in a correct way. + The normalization type can be one of: + - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the CImgDisplay instance + have values in range [0,255]. + - \c 1: Value normalization is always performed (this is the default behavior). + Before displaying an input image, its values will be (virtually) stretched + in range [0,255], so that the contrast of the displayed pixels will be maximum. + Use this mode for images whose minimum and maximum values are not prescribed to known values (e.g. float-valued images). + Note that when normalized versions of images are computed for display purposes, the actual values of these images are not modified. + - \c 2: Value normalization is performed once (on the first image display), then the same normalization coefficients are kept for + next displayed frames. + - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types, the normalization + is done regarding the minimum/maximum values of the type (no normalization occurs then for unsigned char). + For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image data instead. + + **/ + unsigned int normalization() const { + return _normalization; + } + + //! Return title of the associated window as a C-string. + /** + \note Window title may be not visible, depending on the used window manager or if the current display is in fullscreen mode. + **/ + const char *title() const { + return _title; + } + + //! Return width of the associated window. + /** + \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual width of the associated window. + **/ + int window_width() const { + return (int)_window_width; + } + + //! Return height of the associated window. + /** + \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual height of the associated window. + **/ + int window_height() const { + return (int)_window_height; + } + + //! Return X-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_x() const { + return _window_x; + } + + //! Return Y-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_y() const { + return _window_y; + } + + //! Return X-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,width()-1]. + **/ + int mouse_x() const { + return _mouse_x; + } + + //! Return Y-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,height()-1]. + **/ + int mouse_y() const { + return _mouse_y; + } + + //! Return current state of the mouse buttons. + /** + \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned value is set: + - bit \c 0 (value \c 0x1): State of the left mouse button. + - bit \c 1 (value \c 0x2): State of the right mouse button. + - bit \c 2 (value \c 0x4): State of the middle mouse button. + + Several bits can be activated if more than one button are pressed at the same time. + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.button()&1) { // Left button clicked. + ... + } + if (disp.button()&2) { // Right button clicked. + ... + } + if (disp.button()&4) { // Middle button clicked. + ... + } + disp.wait(); + } + \endcode + **/ + unsigned int button() const { + return _button; + } + + //! Return current state of the mouse wheel. + /** + \note + - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled forward or backward. + - Scrolling the wheel forward add \c 1 to the wheel value. + - Scrolling the wheel backward substract \c 1 to the wheel value. + - The returned value cumulates the number of forward of backward scrolls since the creation of the display, or since the + last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset the wheel counter + when an action has been performed regarding the current wheel value. Otherwise, the returned wheel value may be for instance \c 0 + despite the fact that many scrolls have been done (as many in forward as in backward directions). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.wheel()) { + int counter = disp.wheel(); // Read the state of the mouse wheel. + ... // Do what you want with 'counter'. + disp.set_wheel(); // Reset the wheel value to 0. + } + disp.wait(); + } + \endcode + **/ + int wheel() const { + return _wheel; + } + + //! Return one entry from the pressed keys history. + /** + \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry). + \return Keycode of a pressed key or \c 0 for a released key. + \note + - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed, + its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead. + This means that up to the 64 last pressed keys may be read from the pressed keys history. + When a new value is stored, the pressed keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int key(const unsigned int pos=0) const { + return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0; + } + + //! Return one entry from the released keys history. + /** + \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry). + \return Keycode of a released key or \c 0 for a pressed key. + \note + - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released, + its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead. + This means that up to the 64 last released keys may be read from the released keys history. + When a new value is stored, the released keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int released_key(const unsigned int pos=0) const { + return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0; + } + + //! Return keycode corresponding to the specified string. + /** + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB. + \endcode + **/ + static unsigned int keycode(const char *const keycode) { +#define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k; + _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3); + _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7); + _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11); + _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2); + _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6); + _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0); + _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME); + _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W); + _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y); + _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P); + _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN); + _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D); + _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J); + _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER); + _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C); + _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M); + _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT); + _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR); + _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT); + _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT); + _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2); + _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5); + _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8); + _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB); + _cimg_keycode(PADMUL); _cimg_keycode(PADDIV); + return 0; + } + + //! Return the current refresh rate, in frames per second. + /** + \note Returns a significant value when the current instance is used to display successive frames. + It measures the delay between successive calls to frames_per_second(). + **/ + 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; + } + + //@} + //--------------------------------------- + // + //! \name Window Manipulation + //@{ + //--------------------------------------- + +#if cimg_display==0 + + //! Display image on associated window. + /** + \param img Input image to display. + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImg& img) { + return assign(img); + } + +#endif + + //! Display list of images on associated window. + /** + \param list List of images to display. + \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c). + \param align Relative position of aligned images when displaying lists with images of different sizes + (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right). + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { + return display(list.get_append(axis,align)); + } + +#if cimg_display==0 + + //! Show (closed) associated window on the screen. + /** + \note + - Force the associated window of a display to be visible on the screen, even if it has been closed before. + - Using show() on a visible display does nothing. + **/ + CImgDisplay& show() { + return assign(); + } + + //! Close (visible) associated window and make it disappear from the screen. + /** + \note + - A closed display only means the associated window is not visible anymore. This does not mean the display has been destroyed. + Use show() to make the associated window reappear. + - Using close() on a closed display does nothing. + **/ + CImgDisplay& close() { + return assign(); + } + + //! Move associated window to a new location. + /** + \param pos_x X-coordinate of the new window location. + \param pos_y Y-coordinate of the new window location. + \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless). + **/ + CImgDisplay& move(const int pos_x, const int pos_y) { + return assign(pos_x,pos_y); + } + +#endif + + //! Resize display to the size of the associated window. + /** + \param force_redraw Tells if the previous window content must be updated and refreshed as well. + \note + - Calling this method ensures that width() and window_width() become equal, as well as height() and window_height(). + - The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const bool force_redraw=true) { + resize(_window_width,_window_height,force_redraw); + return *this; + } + +#if cimg_display==0 + + //! Resize display to the specified size. + /** + \param width Requested display width. + \param height Requested display height. + \param force_redraw Tells if the previous window content must be updated and refreshed as well. + \note The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) { + return assign(width,height,0,3,force_redraw); + } + +#endif + + //! Resize display to the size of an input image. + /** + \param img Input image to take size from. + \param force_redraw Tells if the previous window content must be resized and updated as well. + \note + - Calling this method ensures that width() and img.width() become equal, as well as height() and img.height(). + - The associated window is also resized to specified dimensions. + **/ + template + CImgDisplay& resize(const CImg& img, const bool force_redraw=true) { + return resize(img._width,img._height,force_redraw); + } + + //! Resize display to the size of another CImgDisplay instance. + /** + \param disp Input display to take size from. + \param force_redraw Tells if the previous window content must be resized and updated as well. + \note + - Calling this method ensures that width() and disp.width() become equal, as well as height() and disp.height(). + - The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { + return resize(disp._width,disp._height,force_redraw); + } + + // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). + template + static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; + float s, curr, old; + s = (float)ws/wd; + poffx = offx; curr = 0; for (unsigned int x = 0; xstd::printf(). + \warning As the first argument is a format string, it is highly recommended to write + \code + disp.set_title("%s",window_title); + \endcode + instead of + \code + disp.set_title(window_title); + \endcode + if \c window_title can be arbitrary, to prevent nasty memory access. + **/ + CImgDisplay& set_title(const char *const format, ...) { + return assign(0,0,format); + } + +#endif + + //! Enable or disable fullscreen mode. + /** + \param is_fullscreen Tells is the fullscreen mode must be activated or not. + \param force_redraw Tells if the previous window content must be displayed as well. + \note + - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the current display + is not modified. + - The screen resolution may be switched to fit the associated window size and ensure it appears the largest as possible. + For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen resolution change + (requires the X11 extensions to be enabled). + **/ + CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) { + if (is_empty() || _is_fullscreen==is_fullscreen) return *this; + return toggle_fullscreen(force_redraw); + } + +#if cimg_display==0 + + //! Toggle fullscreen mode. + /** + \param force_redraw Tells if the previous window content must be displayed as well. + \note Enable fullscreen mode if it was not enabled, and disable it otherwise. + **/ + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + return assign(_width,_height,0,3,force_redraw); + } + + //! Show mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless). + **/ + CImgDisplay& show_mouse() { + return assign(); + } + + //! Hide mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless). + **/ + CImgDisplay& hide_mouse() { + return assign(); + } + + //! Move mouse pointer to a specified location. + /** + \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless). + **/ + CImgDisplay& set_mouse(const int pos_x, const int pos_y) { + return assign(pos_x,pos_y); + } + +#endif + + //! Simulate a mouse button release event. + /** + \note All mouse buttons are considered released at the same time. + **/ + CImgDisplay& set_button() { + _button = 0; + _is_event = true; + return *this; + } + + //! Simulate a mouse button press or release event. + /** + \param button Buttons event code, where each button is associated to a single bit. + \param is_pressed Tells if the mouse button is considered as pressed or released. + **/ + CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) { + const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0; + if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode; + _is_event = buttoncode?true:false; + return *this; + } + + //! Flush all mouse wheel events. + /** + \note Make wheel() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_wheel() { + _wheel = 0; + _is_event = true; + return *this; + } + + //! Simulate a wheel event. + /** + \param amplitude Amplitude of the wheel scrolling to simulate. + \note Make wheel() to return \c amplitude, if called afterwards. + **/ + CImgDisplay& set_wheel(const int amplitude) { + _wheel+=amplitude; + _is_event = amplitude?true:false; + return *this; + } + + //! Flush all key events. + /** + \note Make key() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_key() { + std::memset((void*)_keys,0,sizeof(_keys)); + std::memset((void*)_released_keys,0,sizeof(_released_keys)); + _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_event = true; + return *this; + } + + //! Simulate a keyboard press/release event. + /** + \param keycode Keycode of the associated key. + \param is_pressed Tells if the key is considered as pressed or released. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) { +#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed; + _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3); + _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7); + _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11); + _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2); + _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6); + _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0); + _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME); + _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W); + _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y); + _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P); + _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN); + _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D); + _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J); + _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER); + _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C); + _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M); + _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT); + _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR); + _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT); + _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT); + _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2); + _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5); + _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8); + _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB); + _cimg_set_key(PADMUL); _cimg_set_key(PADDIV); + if (is_pressed) { + if (*_keys) + std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int)); + *_keys = keycode; + if (*_released_keys) { + std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int)); + *_released_keys = 0; + } + } else { + if (*_keys) { + std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int)); + *_keys = 0; + } + if (*_released_keys) + std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int)); + *_released_keys = keycode; + } + _is_event = keycode?true:false; + return *this; + } + + //! Flush all display events. + /** + \note Remove all passed events from the current display. + **/ + CImgDisplay& flush() { + set_key().set_button().set_wheel(); + _is_resized = _is_moved = _is_event = false; + _fps_timer = _fps_frames = _timer = 0; + _fps_fps = 0; + return *this; + } + + //! Wait for any user event occuring on the current display. + CImgDisplay& wait() { + wait(*this); + return *this; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \note Similar to cimg::wait(). + **/ + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::_wait(milliseconds,_timer); + return *this; + } + + //! Wait for any event occuring on the display \c disp1. + static void wait(CImgDisplay& disp1) { + disp1._is_event = 0; + while (!disp1._is_closed && !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_closed || !disp2._is_closed) && + !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_closed || !disp2._is_closed || !disp3._is_closed) && + !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_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, CImgDisplay& disp10) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = 0; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) wait_all(); + } + +#if cimg_display==0 + + //! Wait for any window event occuring in any opened CImgDisplay. + static void wait_all() { + return _no_display_exception(); + } + + //! Render image into internal display buffer. + /** + \param img Input image data to render. + \note + - Convert image data representation into the internal display buffer (architecture-dependent structure). + - The content of the associated window is not modified, until paint() is called. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + template + CImgDisplay& render(const CImg& img) { + return assign(img); + } + + //! Paint internal display buffer on associated window. + /** + \note + - Update the content of the associated window with the internal display buffer, e.g. after a render() call. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + CImgDisplay& paint() { + return assign(); + } + + //! Take a snapshot of the associated window content. + /** + \param[out] img Output snapshot. Can be empty on input. + **/ + template + const CImgDisplay& snapshot(CImg& img) const { + cimg::unused(img); + _no_display_exception(); + return *this; + } +#endif + + // X11-based implementation + //-------------------------- +#if cimg_display==1 + + Atom _wm_window_atom, _wm_protocol_atom; + Window _window, _background_window; + Colormap _colormap; + XImage *_image; + void *_data; +#ifdef cimg_use_xshm + XShmSegmentInfo *_shminfo; +#endif + + static int screen_width() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); + res = DisplayWidth(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width; + else res = DisplayWidth(dpy,DefaultScreen(dpy)); +#else + res = DisplayWidth(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static int screen_height() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); + res = DisplayHeight(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height; + else res = DisplayHeight(dpy,DefaultScreen(dpy)); +#else + res = DisplayHeight(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static void wait_all() { + Display *const dpy = cimg::X11_attr().display; + if (!dpy) return; + XLockDisplay(dpy); + bool flag = true; + XEvent event; + while (flag) { + XNextEvent(dpy,&event); + for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) { + cimg::X11_attr().wins[i]->_handle_events(&event); + if (cimg::X11_attr().wins[i]->_is_event) flag = false; + } + } + XUnlockDisplay(dpy); + } + + void _handle_events(const XEvent *const pevent) { + Display *const dpy = cimg::X11_attr().display; + XEvent event = *pevent; + switch (event.type) { + case ClientMessage : { + if ((int)event.xclient.message_type==(int)_wm_protocol_atom && + (int)event.xclient.data.l[0]==(int)_wm_window_atom) { + XUnmapWindow(cimg::X11_attr().display,_window); + _is_closed = _is_event = true; + } + } break; + case ConfigureNotify : { + while (XCheckWindowEvent(dpy,_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(dpy,_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(dpy,_window,ExposureMask,&event)) {} + _paint(false); + if (_is_fullscreen) { + XWindowAttributes attr; + XGetWindowAttributes(dpy,_window,&attr); + while (attr.map_state!=IsViewable) XSync(dpy,0); + XSetInputFocus(dpy,_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>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1); break; + case 3 : set_button(2); break; + case 2 : set_button(3); break; + } + } while (XCheckWindowEvent(dpy,_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>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1,false); break; + case 3 : set_button(2,false); break; + case 2 : set_button(3,false); break; + case 4 : set_wheel(1); break; + case 5 : set_wheel(-1); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event)); + } break; + case KeyPress : { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,true); + } break; + case KeyRelease : { + char keys_return[32]; // Check that the key has been physically unpressed. + XQueryKeymap(dpy,keys_return); + const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8; + const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1; + if (!is_key_pressed) { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,false); + } + } break; + case EnterNotify: { + while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + } break; + case LeaveNotify : { + while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} + _mouse_x = _mouse_y =-1; _is_event = true; + } break; + case MotionNotify : { + while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + _is_event = true; + } break; + } + } + + static void* _events_thread(void *) { // Only one thread to handle events for all opened display windows. + Display *const dpy = cimg::X11_attr().display; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + for (;;) { + XLockDisplay(dpy); + bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); + if (!event_flag) event_flag = XCheckMaskEvent(dpy, + ExposureMask | StructureNotifyMask | ButtonPressMask| + KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask| + ButtonReleaseMask | KeyReleaseMask,&event); + if (event_flag) + for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) + cimg::X11_attr().wins[i]->_handle_events(&event); + XUnlockDisplay(dpy); + pthread_testcancel(); + cimg::sleep(8); + } + return 0; + } + + void _set_colormap(Colormap& _colormap, const unsigned int dim) { + XColor colormap[256]; + switch (dim) { + case 1 : { // colormap for greyscale images + for (unsigned int index = 0; index<256; ++index) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8); + colormap[index].flags = DoRed | DoGreen | DoBlue; + } + } break; + case 2 : { // colormap for RG images + for (unsigned int index = 0, r = 8; r<256; r+=16) + for (unsigned int g = 8; g<256; g+=16) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].blue = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } break; + default : { // colormap 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) { + colormap[index].pixel = index; + colormap[index].red = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index].blue = (unsigned short)(b<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } + } + XStoreColors(cimg::X11_attr().display,_colormap,colormap,256); + } + + void _map_window() { + Display *const dpy = cimg::X11_attr().display; + bool is_exposed = false, is_mapped = false; + XWindowAttributes attr; + XEvent event; + XMapRaised(dpy,_window); + do { // Wait for the window to be mapped. + XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify : is_mapped = true; break; + case Expose : is_exposed = true; break; + } + } while (!is_exposed || !is_mapped); + do { // Wait for the window to be visible. + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); 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 || !_image) return; + Display *const dpy = cimg::X11_attr().display; + if (wait_expose) { // Send an expose event sticked to display window to force repaint. + static XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = 1; + event.xexpose.display = dpy; + event.xexpose.window = _window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = width(); + event.xexpose.height = height(); + event.xexpose.count = 0; + XSendEvent(dpy,_window,0,0,&event); + } else { // Repaint directly (may be called from the expose event). + GC gc = DefaultGC(dpy,DefaultScreen(dpy)); +#ifdef cimg_use_xshm + if (_shminfo) { + const int completion_type = XShmGetEventBase(dpy) + ShmCompletion; + XEvent event; + XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); + do { XNextEvent(dpy,&event); } while (event.type!=completion_type); // Wait for the image drawing to be completed. + } else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#else + XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#endif + } + } + + template + void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) { + Display *const dpy = cimg::X11_attr().display; + cimg::unused(pixel_type); + +#ifdef cimg_use_xshm + if (_shminfo) { + XShmSegmentInfo *const nshminfo = new XShmSegmentInfo; + XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().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 = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,nshminfo); + XFlush(dpy); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(nshminfo->shmaddr); + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + T *const ndata = (T*)nimage->data; + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(dpy,_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*)std::malloc(ndimx*ndimy*sizeof(T)); + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + _data = (void*)ndata; + XDestroyImage(_image); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + if (!_is_fullscreen || _is_closed) return; + Display *const dpy = cimg::X11_attr().display; + _background_window = 0; + +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(dpy,&foo,&foo)) { + XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation); + if (!cimg::X11_attr().resolutions) { + cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo); + cimg::X11_attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11_attr().resolutions) { + cimg::X11_attr().curr_resolution = 0; + for (unsigned int i = 0; i=_width && nh>=_height && + nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height)) + cimg::X11_attr().curr_resolution = i; + } + if (cimg::X11_attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), + cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + } + } + } + if (!cimg::X11_attr().resolutions) + cimg::warn(_cimgdisplay_instance + "init_fullscreen(): Xrandr extension not supported by the X server.", + cimgdisplay_instance); +#endif + + const unsigned int sx = screen_width(), sy = screen_height(); + if (sx==_width && sy==_height) return; + XSetWindowAttributes winattr; + winattr.override_redirect = 1; + _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, + InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + const unsigned long buf_size = (unsigned long)sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + void *background_data = std::malloc(buf_size); + std::memset(background_data,0,buf_size); + XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,(char*)background_data,sx,sy,8,0); + XEvent event; + XSelectInput(dpy,_background_window,StructureNotifyMask); + XMapRaised(dpy,_background_window); + do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); + GC gc = DefaultGC(dpy,DefaultScreen(dpy)); +#ifdef cimg_use_xshm + if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0); + else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy); +#else + XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy); +#endif + XWindowAttributes attr; + XGetWindowAttributes(dpy,_background_window,&attr); + while (attr.map_state!=IsViewable) XSync(dpy,0); + XDestroyImage(background_image); + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + Display *const dpy = cimg::X11_attr().display; + XUngrabKeyboard(dpy,CurrentTime); +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + cimg::X11_attr().curr_resolution = 0; + } +#endif + if (_background_window) XDestroyWindow(dpy,_background_window); + _background_window = 0; + _is_fullscreen = false; + } + + static int _assign_xshm(Display *dpy, XErrorEvent *error) { + cimg::unused(dpy,error); + cimg::X11_attr().is_shm_enabled = false; + return 0; + } + + void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous display window if existing + if (!is_empty()) assign(); + + // Open X11 display and retrieve graphical properties. + Display* &dpy = cimg::X11_attr().display; + if (!dpy) { + static const int xinit_status = XInitThreads(); + cimg::unused(xinit_status); + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Failed to open X11 display.", + cimgdisplay_instance); + + cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy)); + if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Invalid %u bits screen mode detected " + "(only 8, 16, 24 and 32 bits modes are managed).", + cimgdisplay_instance, + cimg::X11_attr().nb_bits); + XVisualInfo vtemplate; + vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy))); + int nb_visuals; + XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); + if (vinfo && vinfo->red_maskblue_mask) cimg::X11_attr().is_blue_first = true; + cimg::X11_attr().byte_order = ImageByteOrder(dpy); + XFree(vinfo); + + XLockDisplay(dpy); + cimg::X11_attr().event_thread = new pthread_t; + pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0); + } else XLockDisplay(dpy); + + // Set display variables. + _width = cimg::min(dimw,(unsigned int)screen_width()); + _height = cimg::min(dimh,(unsigned int)screen_height()); + _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 LUT, if 8bits display) + if (_is_fullscreen) { + if (!_is_closed) _init_fullscreen(); + const unsigned int sx = screen_width(), sy = screen_height(); + XSetWindowAttributes winattr; + winattr.override_redirect = 1; + _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0, + InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + } else + _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); + + XSelectInput(dpy,_window, + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + + XStoreName(dpy,_window,_title?_title:" "); + if (cimg::X11_attr().nb_bits==8) { + _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll); + _set_colormap(_colormap,3); + XSetWindowColormap(dpy,_window,_colormap); + } + + static const char *const _window_class = cimg_appname; + XClassHint *const window_class = XAllocClassHint(); + window_class->res_name = (char*)_window_class; + window_class->res_class = (char*)_window_class; + XSetClassHint(dpy,_window,window_class); + XFree(window_class); + + _window_width = _width; + _window_height = _height; + + // Create XImage +#ifdef cimg_use_xshm + _shminfo = 0; + if (XShmQueryExtension(dpy)) { + _shminfo = new XShmSegmentInfo; + _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,_shminfo,_width,_height); + if (!_image) { delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,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 = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,_shminfo); + XSync(dpy,0); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; + } + } + } + } + } + if (!_shminfo) +#endif + { + const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + _data = std::malloc(buf_size); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,_width,_height,8,0); + } + + _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0); + _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0); + XSetWMProtocols(dpy,_window,&_wm_window_atom,1); + + if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime); + cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this; + if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type::min(); } + XUnlockDisplay(dpy); + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + + // Remove display window from event thread list. + unsigned int i; + for (i = 0; ishmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = 0; + } else +#endif + XDestroyImage(_image); + _data = 0; _image = 0; + if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap); + _colormap = 0; + XSync(dpy,0); + + // Reset display variables + 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(dpy); + if (!cimg::X11_attr().nb_wins) { + // Kill event thread + //pthread_cancel(*cimg::X11_attr().event_thread); + //XUnlockDisplay(cimg::X11_attr().display); + //pthread_join(*cimg::X11_attr().event_thread,0); + //delete cimg::X11_attr().event_thread; + //cimg::X11_attr().event_thread = 0; + // XUnlockDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required). + // XCloseDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required). + //cimg::X11_attr().display = 0; + } + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const 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; + std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*(unsigned long)_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &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.min_max(_max); + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + cimg::X11_attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*(unsigned long)_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + Display *const dpy = cimg::X11_attr().display; + 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(dpy); + if (_window_width!=dimx || _window_height!=dimy) { + XWindowAttributes attr; + for (unsigned int i = 0; i<10; ++i) { + XResizeWindow(dpy,_window,dimx,dimy); + XGetWindowAttributes(dpy,_window,&attr); + if (attr.width==(int)dimx && attr.height==(int)dimy) break; + cimg::wait(5); + } + } + if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { + case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } + } + _window_width = _width = dimx; _window_height = _height = dimy; + _is_resized = false; + XUnlockDisplay(dpy); + if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + void *image_data = std::malloc(buf_size); + std::memcpy(image_data,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,image_data,buf_size); + std::free(image_data); + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + if (_is_fullscreen) _init_fullscreen(); + _map_window(); + _is_closed = false; + XUnlockDisplay(dpy); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + if (_is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(dpy,_window); + _window_x = _window_y = -1; + _is_closed = true; + XUnlockDisplay(dpy); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + show(); + XLockDisplay(dpy); + XMoveWindow(dpy,_window,posx,posy); + _window_x = posx; _window_y = posy; + _is_moved = false; + XUnlockDisplay(dpy); + return paint(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + XUndefineCursor(dpy,_window); + XUnlockDisplay(dpy); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0); + XFreePixmap(dpy,pix); + XDefineCursor(dpy,_window,cur); + XUnlockDisplay(dpy); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); + _mouse_x = posx; _mouse_y = posy; + _is_moved = false; + XSync(dpy,0); + XUnlockDisplay(dpy); + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char tmp[1024] = { 0 }; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,sizeof(tmp),format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) return *this; + delete[] _title; + const unsigned int s = std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + XStoreName(dpy,_window,tmp); + XUnlockDisplay(dpy); + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(false); + } + + CImgDisplay& paint(const bool wait_expose=true) { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + XLockDisplay(dpy); + _paint(wait_expose); + XUnlockDisplay(dpy); + return *this; + } + + template + CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) return render(img.get_resize(_width,_height,1,-100,1)); + if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) { + static const CImg::ucharT> default_colormap = CImg::ucharT>::default_LUT256(); + return render(img.get_index(default_colormap,1,false)); + } + + Display *const dpy = cimg::X11_attr().display; + const T + *data1 = img._data, + *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1; + + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + XLockDisplay(dpy); + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, no normalization + _set_colormap(_colormap,img._spectrum); + unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++); + break; + case 2 : for (unsigned long xy = (unsigned long)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 long xy = (unsigned long)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[(unsigned long)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)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 long xy = (unsigned long)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::X11_attr().byte_order) for (unsigned long xy = (unsigned long)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 long xy = (unsigned long)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::X11_attr().byte_order) for (unsigned long xy = (unsigned long)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 long xy = (unsigned long)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[(unsigned long)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + else + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + else + for (unsigned long xy = (unsigned long)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._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + *(ptrd++) = 0; + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + break; + default : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data3++); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(ptrd++) = (unsigned char)*(data3++); + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.0f); + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, with normalization + _set_colormap(_colormap,img._spectrum); + unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char R = (unsigned char)((*(data1++)-_min)*mm); + *(ptrd++) = R; + } break; + case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++)-_min)*mm), + G = (unsigned char)((*(data2++)-_min)*mm); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++)-_min)*mm), + G = (unsigned char)((*(data2++)-_min)*mm), + B = (unsigned char)((*(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[(unsigned long)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2; + *(ptrd++) = (val&M) | (G>>3); + *(ptrd++) = (G<<5) | (val>>3); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2; + *(ptrd++) = (G<<5) | (val>>3); + *(ptrd++) = (val&M) | (G>>3); + } + break; + case 2 : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; + *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); + *(ptrd++) = (G<<5); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; + *(ptrd++) = (G<<5); + *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); + } + break; + default : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; + *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); + *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; + *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3); + *(ptrd++) = ((unsigned char)((*(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[(unsigned long)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + *(ptrd++) = (val<<24) | (val<<16) | (val<<8); + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++)-_min)*mm)<<16) | + ((unsigned char)((*(data2++)-_min)*mm)<<8); + else + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data2++)-_min)*mm)<<16) | + ((unsigned char)((*(data1++)-_min)*mm)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++)-_min)*mm)<<16) | + ((unsigned char)((*(data2++)-_min)*mm)<<8) | + (unsigned char)((*(data3++)-_min)*mm); + else + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data3++)-_min)*mm)<<24) | + ((unsigned char)((*(data2++)-_min)*mm)<<16) | + ((unsigned char)((*(data1++)-_min)*mm)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + (*ptrd++) = 0; + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = val; + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = 0; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + (*ptrd++) = 0; + (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); + (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); + (*ptrd++) = 0; + } + break; + default : + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + (*ptrd++) = 0; + (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); + (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); + (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm); + (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); + (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); + (*ptrd++) = 0; + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; } + } + } + } + XUnlockDisplay(dpy); + return *this; + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned char *ptrs = (unsigned char*)_data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + switch (cimg::X11_attr().nb_bits) { + case 8 : { + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = *(ptrs++); + *(data1++) = (T)(val&0xe0); + *(data2++) = (T)((val&0x1c)<<3); + *(data3++) = (T)(val<<6); + } + } break; + case 16 : { + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val0 = *(ptrs++), val1 = *(ptrs++); + *(data1++) = (T)(val0&0xf8); + *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5)); + *(data3++) = (T)(val1<<3); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned short val0 = *(ptrs++), val1 = *(ptrs++); + *(data1++) = (T)(val1&0xf8); + *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5)); + *(data3++) = (T)(val0<<3); + } + } break; + default : { + if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + ++ptrs; + *(data1++) = (T)*(ptrs++); + *(data2++) = (T)*(ptrs++); + *(data3++) = (T)*(ptrs++); + } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + *(data3++) = (T)*(ptrs++); + *(data2++) = (T)*(ptrs++); + *(data1++) = (T)*(ptrs++); + ++ptrs; + } + } + } + return *this; + } + + // Windows-based implementation. + //------------------------------- +#elif cimg_display==2 + + bool _is_mouse_tracked, _is_cursor_visible; + HANDLE _thread, _is_created, _mutex; + HWND _window, _background_window; + CLIENTCREATESTRUCT _ccs; + unsigned int *_data; + DEVMODE _curr_mode; + BITMAPINFO _bmi; + HDC _hdc; + + static int screen_width() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsWidth; + } + + static int screen_height() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsHeight; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE); + } + + static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay *const 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; + disp->set_button().set_key(0).set_key(0,false)._is_closed = true; + ReleaseMutex(disp->_mutex); + ShowWindow(disp->_window,SW_HIDE); + disp->_is_event = true; + SetEvent(cimg::Win32_attr().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::Win32_attr().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::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_PAINT : + disp->paint(); + break; + case WM_KEYDOWN : + disp->set_key((unsigned int)wParam); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_KEYUP : + disp->set_key((unsigned int)wParam,false); + SetEvent(cimg::Win32_attr().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->_is_mouse_tracked) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = disp->_window; + if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true; + } +#endif + if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height()) + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } break; + case WM_MOUSELEAVE : { + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_mouse_tracked = false; + } break; + case WM_LBUTTONDOWN : + disp->set_button(1); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONDOWN : + disp->set_button(2); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONDOWN : + disp->set_button(3); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_LBUTTONUP : + disp->set_button(1,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONUP : + disp->set_button(2,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONUP : + disp->set_button(3,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case 0x020A : // WM_MOUSEWHEEL: + disp->set_wheel((int)((short)HIWORD(wParam))/120); + SetEvent(cimg::Win32_attr().wait_event); + case WM_SETCURSOR : + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); + else while (ShowCursor(FALSE)>=0); + break; + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]); + const char *const 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 = -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[(unsigned long)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_width(), sy = screen_height(); + 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->_is_created); + while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); + return 0; + } + + CImgDisplay& _update_window_pos() { + if (_is_closed) _window_x = _window_y = -1; + else { + 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; + } + return *this; + } + + void _init_fullscreen() { + _background_window = 0; + if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; + else { + 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_width(), sy = screen_height(); + 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); + } + } + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + 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 *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + _width = cimg::min(dimw,(unsigned int)screen_width()); + _height = cimg::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = 0; + _is_closed = closed_flag; + _is_cursor_visible = true; + _is_mouse_tracked = false; + _title = tmp_title; + flush(); + if (_is_fullscreen) _init_fullscreen(); + + // Create event thread + void *const arg = (void*)(new void*[2]); + ((void**)arg)[0] = (void*)this; + ((void**)arg)[1] = (void*)_title; + unsigned long ThreadID = 0; + _mutex = CreateMutex(0,FALSE,0); + _is_created = CreateEvent(0,FALSE,FALSE,0); + _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); + WaitForSingleObject(_is_created,INFINITE); + return *this; + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + DestroyWindow(_window); + TerminateThread(_thread,0); + delete[] _data; + delete[] _title; + _data = 0; + _title = 0; + 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 *const 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; + std::memset(_data,0,sizeof(unsigned int)*_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &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.min_max(_max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_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 *const ndata = new unsigned int[dimx*dimy]; + if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); + else 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_width()-_width)/2,(screen_height()-_height)/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const unsigned long buf_size = _width*_height*4UL; + void *odata = std::malloc(buf_size); + std::memcpy(odata,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,odata,buf_size); + std::free(odata); + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + ShowWindow(_window,SW_SHOW); + _update_window_pos(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + _is_closed = true; + if (_is_fullscreen) _desinit_fullscreen(); + ShowWindow(_window,SW_HIDE); + _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; + _is_cursor_visible = true; + SendMessage(_window,WM_SETCURSOR,0,0); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = 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) return *this; + _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 *const format, ...) { + if (is_empty()) return *this; + char tmp[1024] = { 0 }; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,sizeof(tmp),format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) return *this; + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + SetWindowTextA(_window, tmp); + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(); + } + + CImgDisplay& paint() { + if (_is_closed) return *this; + WaitForSingleObject(_mutex,INFINITE); + SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS); + ReleaseMutex(_mutex); + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + + if (is_empty()) return *this; + 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._spectrum>=2)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1; + + WaitForSingleObject(_mutex,INFINITE); + unsigned int + *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[(unsigned long)img._width*img._height], + *ptrd = ndata; + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (img._spectrum) { + case 1 : { + for (unsigned long xy = (unsigned long)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 long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + } break; + default : { + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.0f); + switch (img._spectrum) { + case 1 : { + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + } break; + case 2 : { + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++)-_min)*mm), + G = (unsigned char)((*(data2++)-_min)*mm); + *(ptrd++) = (R<<16) | (G<<8); + } + } break; + default : { + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++)-_min)*mm), + G = (unsigned char)((*(data2++)-_min)*mm), + B = (unsigned char)((*(data3++)-_min)*mm); + *(ptrd++) = (R<<16) | (G<<8) | B; + } + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; } + ReleaseMutex(_mutex); + return *this; + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned int *ptrs = _data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (T)(unsigned char)(val>>16); + *(data2++) = (T)(unsigned char)((val>>8)&0xFF); + *(data3++) = (T)(unsigned char)(val&0xFF); + } + return *this; + } +#endif + + //@} + }; + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \c CImg, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), while the number of channels + is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \c CImgList rather than simple images \c CImg. + + Thus, the \c CImg class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1d scalar signal, 2d color images, ...). + Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T: + fully supported template types are the basic C++ types: unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \c CImg structure contains \e six fields: + - \c _width defines the number of \a columns of the image (size along the X-axis). + - \c _height defines the number of \a rows of the image (size along the Y-axis). + - \c _depth defines the number of \a slices of the image (size along the Z-axis). + - \c _spectrum defines the number of \a channels of the image (size along the C-axis). + - \c _data defines a \a pointer to the \a pixel \a data (of type \c T). + - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with + another image. + + You can access these fields publicly although it is recommended to use the dedicated functions + width(), height(), depth(), spectrum() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used: + + - Construct images from arbitrary dimensions: + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note: images pixels are not automatically initialized to 0. You may use the function \c fill() to + do it, or use the specific constructor taking 5 parameters like this: + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames: + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". + - \b Note: You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). + + - Construct images from C-style arrays: + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \c CImg class contains a lot of functions that operates on images. + Some of the most useful are: + + - operator()(): allows to access or write pixel values. + - display(): displays the image in a new window. + **/ + template + struct CImg { + + unsigned int _width, _height, _depth, _spectrum; + bool _is_shared; + T *_data; + + //! Simple iterator type, to loop through each pixel value of an image instance. + /** + \note + - The \c CImg::iterator type is defined to be a T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + CImg img("reference.jpg"); // Load image from file. + for (CImg::iterator it = img.begin(), it::const_iterator type is defined to be a \c const \c T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + const CImg img("reference.jpg"); // Load image from file. + float sum = 0; + for (CImg::iterator it = img.begin(), it::value_type type of a \c CImg is defined to be a \c T. + - \c CImg::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif +#ifdef cimg_plugin1 +#include cimg_plugin1 +#endif +#ifdef cimg_plugin2 +#include cimg_plugin2 +#endif +#ifdef cimg_plugin3 +#include cimg_plugin3 +#endif +#ifdef cimg_plugin4 +#include cimg_plugin4 +#endif +#ifdef cimg_plugin5 +#include cimg_plugin5 +#endif +#ifdef cimg_plugin6 +#include cimg_plugin6 +#endif +#ifdef cimg_plugin7 +#include cimg_plugin7 +#endif +#ifdef cimg_plugin8 +#include cimg_plugin8 +#endif + + //@} + //--------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //--------------------------------------------------------- + + //! Destroy image. + /** + \note + - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances. + - Destroying an empty or shared image does nothing actually. + \warning + - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image + that shares its buffer with the destroyed instance, in order to avoid further invalid memory access (to a deallocated buffer). + **/ + ~CImg() { + if (!_is_shared) delete[] _data; + } + + //! Construct empty image. + /** + \note + - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum() + are set to \c 0, as well as its pixel buffer pointer data(). + - An empty image may be re-assigned afterwards, e.g. with the family of assign(unsigned int,unsigned int,unsigned int,unsigned int) methods, + or by operator=(const CImg&). In all cases, the type of pixels stays \c T. + - An empty image is never shared. + \par Example + \code + CImg img1, img2; // Construct two empty images. + img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image. + img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'. + img2.assign(); // Re-assign 'img2' to be an empty image again. + \endcode + **/ + CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} + + //! Construct image with specified size. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \note + - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() for each constructed image instance. + - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of an \e empty image. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory). + \warning + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values during construction (e.g. with \c 0), use constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead. + \par Example + \code + CImg img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values. + CImg img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'. + \endcode + **/ + explicit CImg(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1): + _is_shared(false) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value Initialization value. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), + but it also fills the pixel buffer with the specified \c value. + \warning + - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels (e.g. RGB vector, for color images). + For this task, you may use fillC() after construction. + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value): + _is_shared(false) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + fill(value); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, and initialize pixel + values from the specified sequence of integers \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be an \e integer). + \param value1 Second value of the initialization sequence (must be an \e integer). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \warning + - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence. + Otherwise, the constructor may crash or fill your image pixels with garbage. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image. + 0,255,0,255, // Set the 4 values for the red component. + 0,0,255,255, // Set the 4 values for the green component. + 64,64,64,64); // Set the 4 values for the blue component. + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _CImg_stdarg(img,a0,a1,N,t) { \ + unsigned long _siz = (unsigned long)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(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int); + } + + //! Construct image with specified size and initialize pixel values from a sequence of doubles. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, and initialize pixel + values from the specified sequence of doubles \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be a \e double). + \param value1 Second value of the initialization sequence (must be a \e double). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but + takes a sequence of double values instead of integers. + \warning + - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence. + Otherwise, the constructor may crash or fill your image with garbage. + For instance, the code below will probably crash on most platforms: + \code + const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! + \endcode + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double); + } + + //! Construct image with specified size and initialize pixel values from a value string. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, and initializes pixel + values from the specified string \c values. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param values Value string describing the way pixel values are set. + \param repeat_values Tells if the value filling process is repeated over the image. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with values described in the value string \c values. + - Value string \c values may describe two different filling processes: + - Either \c values is a sequences of values assigned to the image pixels, as in "1,2,3,7,8,2". + In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence. + - Either, \c values is a formula, as in "cos(x/10)*sin(y/20)". In this case, parameter \c repeat_values is pointless. + - For both cases, specifying \c repeat_values is mandatory. It disambiguates the possible overloading of constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a const char*. + - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. + \par Example + \code + const CImg img1(129,129,1,3,"0,64,128,192,255",true), // Construct image filled from a value sequence. + img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image filled from a formula. + (img1,img2).display(); + \endcode + \image html ref_constructor2.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values):_is_shared(false) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + fill(values,repeat_values); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, and initializes pixel + values from the specified \c t* memory buffer. + \param values Pointer to the input memory buffer. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param is_shared Tells if input memory buffer must be shared by the current instance. + \note + - If \c is_shared is \c false, the image instance allocates its own pixel buffer, and values from the specified input buffer + are copied to the instance buffer. If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy. + - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its own pixel buffer. This case + requires that types \c T and \c t are the same. Later, destroying such a shared image will not deallocate the pixel buffer, + this task being obviously charged to the initial buffer allocator. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory). + \warning + - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() (e.g. already deallocated). + \par Example + \code + unsigned char tab[256*256] = { 0 }; + CImg img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'. + img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab'. + tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1'. + \endcode + **/ + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer " + "(pixel types are different).", + cimg_instance, + size_x,size_y,size_z,size_c,CImg::pixel_type()); + } + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + + } + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; + if (_is_shared) _data = const_cast(values); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + std::memcpy(_data,values,siz*sizeof(T)); } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from reading an image file. + /** + Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from an image file. + \param filename Filename, as a C-string. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image + dimensions and pixel values from the specified image file. + - The recognition of the image file format by %CImg higly depends on the tools installed on your system + and on the external libraries you used to link your code against. + - Considered pixel type \c T should better fit the file format specification, or data loss may occur during file load + (e.g. constructing a \c CImg from a float-valued image file). + - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not recognized. + \par Example + \code + const CImg img("reference.jpg"); + img.display(); + \endcode + \image html ref_image.jpg + **/ + explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(filename); + } + + //! Construct image copy. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance. + \param img Input image to copy. + \note + - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the input image \c img. + - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also \e shared, + and shares its pixel buffer with \c img. + Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img. + This behavior is needful to allow functions to return shared images. + - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input image \c img + into its buffer. The copied pixel values may be eventually statically casted if types \c T and \c t are different. + - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than with different types. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. not enough available memory). + **/ + template + CImg(const CImg& img):_is_shared(false) { + const unsigned long siz = img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image copy \specialization. + CImg(const CImg& img) { + const unsigned long siz = img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = img._is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Advanced copy constructor. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance, + while forcing the shared state of the constructed copy. + \param img Input image to copy. + \param is_shared Tells about the shared state of the constructed copy. + \note + - Similar to CImg(const CImg&), except that it allows to decide the shared state of + the constructed image, which does not depend anymore on the shared state of the input image \c img: + - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img. + For that case, the pixel types \c T and \c t \e must be the same. + - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input image \c img is + shared or not. + - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t. + **/ + template + CImg(const CImg& img, const bool is_shared):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a shared instance from a " + "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", + cimg_instance, + CImg::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); + } + const unsigned long siz = img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Advanced copy constructor \specialization. + CImg(const CImg& img, const bool is_shared) { + const unsigned long siz = img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image with dimensions borrowed from another image. + /** + Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing \c CImg instance. + \param img Input image from which dimensions are borrowed. + \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions + (\e not its pixel values) from an existing \c CImg instance. + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg&,const char*,T) instead. + \par Example + \code + const CImg img1(256,128,1,3), // 'img1' is a 256x128x1x3 image. + img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image. + img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image. + img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0'). + \endcode + **/ + template + CImg(const CImg& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values. + /** + Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing \c CImg instance, + and set all pixel values to specified \c value. + \param img Input image from which dimensions are borrowed. + \param dimensions String describing the image size along the X,Y,Z and V-dimensions. + \param value Value used for initialization. + \note + - Similar to CImg(const CImg&,const char*), but it also fills the pixel buffer with the specified \c value. + **/ + template + CImg(const CImg& img, const char *const dimensions, const T value): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions).fill(value); + } + + //! Construct image from a display window. + /** + Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance. + \param disp Input display window. + \note + - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay. + - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 (i.e. a 2d color image). + - The image pixels are read as 8-bits RGB values. + **/ + explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + disp.snapshot(*this); + } + + //! Construct empty image \inplace. + /** + In-place version of the default constructor CImg(). It simply resets the instance to an empty image. + **/ + CImg& assign() { + if (!_is_shared) delete[] _data; + _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; + return *this; + } + + //! Construct image with specified size \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (!siz) return assign(); + const unsigned long curr_siz = size(); + if (siz!=curr_siz) { + if (_is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignement request of shared instance from specified image (%u,%u,%u,%u).", + cimg_instance, + size_x,size_y,size_z,size_c); + else { + delete[] _data; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + } + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + return *this; + } + + //! Construct image with specified size and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value) { + return assign(size_x,size_y,size_z,size_c).fill(value); + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a value string \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values) { + return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \inplace. + /** + In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). + **/ + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (!values || !siz) return assign(); + assign(size_x,size_y,size_z,size_c); + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (!values || !siz) return assign(); + const unsigned long curr_siz = size(); + if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); + if (_is_shared || values+siz<_data || values>=_data+size()) { + assign(size_x,size_y,size_z,size_c); + if (_is_shared) std::memmove(_data,values,siz*sizeof(T)); + else std::memcpy(_data,values,siz*sizeof(T)); + } else { + T *new_data = 0; + try { new_data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c); + } + std::memcpy(new_data,values,siz*sizeof(T)); + delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + } + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + if (is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from (%s*) buffer" + "(pixel types are different).", + cimg_instance, + CImg::pixel_type()); + return assign(values,size_x,size_y,size_z,size_c); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; + if (!values || !siz) { + if (is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from (null) or empty buffer.", + cimg_instance); + else return assign(); + } + if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } + else { + if (!_is_shared) { + if (values+siz<_data || values>=_data+size()) assign(); + else cimg::warn(_cimg_instance + "assign(): Shared image instance has overlapping memory.", + cimg_instance); + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; + _data = const_cast(values); + } + return *this; + } + + //! Construct image from reading an image file \inplace. + /** + In-place version of the constructor CImg(const char*). + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! Construct image copy \inplace. + /** + In-place version of the constructor CImg(const CImg&). + **/ + template + CImg& assign(const CImg& img) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum); + } + + //! In-place version of the advanced copy constructor. + /** + In-place version of the constructor CImg(const CImg&,bool). + **/ + template + CImg& assign(const CImg& img, const bool is_shared) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); + } + + //! Construct image with dimensions borrowed from another image \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions) { + if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum); + unsigned int siz[4] = { 0,1,1,1 }, k = 0; + for (const char *s = dimensions; *s && k<4; ++k) { + char item[256] = { 0 }; + if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item); + if (*s) { + unsigned int val = 0; char sep = 0; + if (std::sscanf(s,"%u%c",&val,&sep)>0) { + if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100; + else siz[k] = val; + while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s; + } else switch (cimg::uncase(*s)) { + case 'x' : case 'w' : siz[k] = img._width; ++s; break; + case 'y' : case 'h' : siz[k] = img._height; ++s; break; + case 'z' : case 'd' : siz[k] = img._depth; ++s; break; + case 'c' : case 's' : siz[k] = img._spectrum; ++s; break; + default : + throw CImgArgumentException(_cimg_instance + "assign(): Invalid character '%c' detected in specified dimension string '%s'.", + cimg_instance, + *s,dimensions); + } + } + } + return assign(siz[0],siz[1],siz[2],siz[3]); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*,T). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions, const T value) { + return assign(img,dimensions).fill(value); + } + + //! Construct image from a display window \inplace. + /** + In-place version of the constructor CImg(const CImgDisplay&). + **/ + CImg& assign(const CImgDisplay &disp) { + disp.snapshot(*this); + return *this; + } + + //! Construct empty image \inplace. + /** + Equivalent to assign(). + \note + - It has been defined for compatibility with STL naming conventions. + **/ + CImg& clear() { + return assign(); + } + + //! Transfer content of an image instance into another one. + /** + Transfer the dimensions and the pixel buffer content of an image instance into another one, + and replace instance by an empty image. It avoids the copy of the pixel buffer + when possible. + \param img Destination image. + \note + - Pixel types \c T and \c t of source and destination images can be different, though the process is designed to be + instantaneous when \c T and \c t are the same. + \par Example + \code + CImg src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'. + dest(16,16); // Construct a 16x16x1x1 (scalar) image. + src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image. + \endcode + **/ + template + CImg& move_to(CImg& img) { + img.assign(*this); + assign(); + return img; + } + + //! Transfer content of an image instance into another one \specialization. + CImg& move_to(CImg& img) { + if (_is_shared || img._is_shared) img.assign(*this); + else swap(img); + assign(); + return img; + } + + //! Transfer content of an image instance into a new image in an image list. + /** + Transfer the dimensions and the pixel buffer content of an image instance + into a newly inserted image at position \c pos in specified \c CImgList instance. + \param list Destination list. + \param pos Position of the newly inserted image in the list. + \note + - When optionnal parameter \c pos is ommited, the image instance is transfered as a new + image at the end of the specified \c list. + - It is convenient to sequentially insert new images into image lists, with no + additional copies of memory buffer. + \par Example + \code + CImgList list; // Construct an empty image list. + CImg img("reference.jpg"); // Read image from filename. + img.move_to(list); // Transfer image content as a new item in the list (no buffer copy). + \endcode + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos=~0U) { + const unsigned int npos = pos>list._width?list._width:pos; + move_to(list.insert(1,npos)[npos]); + return list; + } + + //! Swap fields of two image instances. + /** + \param img Image to swap fields with. + \note + - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing + with algorithms requiring two swapping buffers. + \par Example + \code + CImg img1("lena.jpg"), + img2("milla.jpg"); + img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena'. + \endcode + **/ + CImg& swap(CImg& img) { + cimg::swap(_width,img._width); + cimg::swap(_height,img._height); + cimg::swap(_depth,img._depth); + cimg::swap(_spectrum,img._spectrum); + cimg::swap(_data,img._data); + cimg::swap(_is_shared,img._is_shared); + return img; + } + + //! Return a reference to an empty image. + /** + \note + This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, e.g. + \code + void f(const int x=0, const int y=0, const CImg& img=CImg::empty()); + \endcode + **/ + static CImg& empty() { + static CImg _empty; + return _empty.assign(); + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Access to a pixel value. + /** + Return a reference to a located pixel value of the image instance, + being possibly \e const, whether the image instance is \e const or not. + This is the standard method to get/set pixel values in \c CImg images. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Range of pixel coordinates start from (0,0,0,0) to (width()-1,height()-1,depth()-1,spectrum()-1). + - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the corresponding dimension + is equal to \c 1. + For instance, pixels of a 2d image (depth() equal to \c 1) can be accessed by img(x,y,c) instead of img(x,y,0,c). + \warning + - There is \e no boundary checking done in this operator, to make it as fast as possible. + You \e must take care of out-of-bounds access by yourself, if necessary. + For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary checking operations + in this operator. In that case, warning messages will be printed on the error output when accessing out-of-bounds pixels. + \par Example + \code + CImg img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'. + const float + valR = img(10,10,0,0), // Read red value at coordinates (10,10). + valG = img(10,10,0,1), // Read green value at coordinates (10,10) + valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted). + avg = (valR + valG + valB)/3; // Compute average pixel value. + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value. + \endcode + **/ +#if cimg_verbosity>=3 + T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + const unsigned long off = (unsigned long)offset(x,y,z,c); + if (!_data || off>=size()) { + cimg::warn(_cimg_instance + "operator(): Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].", + cimg_instance, + x,y,z,c,off); + return *_data; + } + else return _data[off]; + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->operator()(x,y,z,c); + } + + //! Access to a pixel value. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param wh Precomputed offset, must be equal to width()*\ref height(). + \param whd Precomputed offset, must be equal to width()*\ref height()*\ref depth(). + \note + - Similar to (but faster than) operator()(). + It uses precomputed offsets to optimize memory access. You may use it to optimize + the reading/writing of several pixel values in the same image (e.g. in a loop). + **/ + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const unsigned long wh, const unsigned long whd=0) { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const unsigned long wh, const unsigned long whd=0) const { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } +#else + T& operator()(const unsigned int x) { + return _data[x]; + } + + const T& operator()(const unsigned int x) const { + return _data[x]; + } + + T& operator()(const unsigned int x, const unsigned int y) { + return _data[x + y*_width]; + } + + const T& operator()(const unsigned int x, const unsigned int y) const { + return _data[x + y*_width]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { + return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const { + return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) { + return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const { + return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const unsigned long wh) { + return _data[x + y*_width + z*wh]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const unsigned long wh) const { + return _data[x + y*_width + z*wh]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const unsigned long wh, const unsigned long whd) { + return _data[x + y*_width + z*wh + c*whd]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const unsigned long wh, const unsigned long whd) const { + return _data[x + y*_width + z*wh + c*whd]; + } +#endif + + //! Implicitely cast an image into a \c T*. + /** + Implicitely cast a \c CImg instance into a \c T* or \c const \c T* pointer, whether the image instance + is \e const or not. The returned pointer points on the first value of the image pixel buffer. + \note + - It simply returns the pointer data() to the pixel buffer. + - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g. + \code + CImg img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image. + if (img1) { // Test succeeds, 'img1' is not an empty image. + if (!img2) { // Test succeeds, 'img2' is an empty image. + std::printf("'img1' is not empty, 'img2' is empty."); + } + } + \endcode + - It also allows to use brackets to access pixel values, without need for a \c CImg::operator[](), e.g. + \code + CImg img(100,100); + const float value = img[99]; // Access to value of the last pixel on the first row. + img[510] = 255; // Set pixel value at (10,5). + \endcode + **/ + operator T*() { + return _data; + } + + //! Implicitely cast an image into a \c T* \const. + operator const T*() const { + return _data; + } + + //! Assign a value to all image pixels. + /** + Assign specified \c value to each pixel value of the image instance. + \param value Value that will be assigned to image pixels. + \note + - The image size is never modified. + - The \c value may be casted to pixel type \c T if necessary. + \par Example + \code + CImg img(100,100); // Declare image (with garbage values). + img = 0; // Set all pixel values to '0'. + img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char'). + \endcode + **/ + CImg& operator=(const T value) { + return fill(value); + } + + //! Assign pixels values from a specified expression. + /** + Initialize all pixel values from the specified string \c expression. + \param expression Value string describing the way pixel values are set. + \note + - String parameter \c expression may describe different things: + - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"), + the pixel values are set from specified \c expression and the image size is not modified. + - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and replace the image instance. + The image size is modified if necessary. + \par Example + \code + CImg img1(100,100), img2(img1), img3(img1); // Declare three 100x100 scalar images with unitialized pixel values. + img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence. + img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula. + img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified). + (img1,img2,img3).display(); + \endcode + \image html ref_operator_eq.jpg + **/ + CImg& operator=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + load(expression); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Copy an image into the current image instance. + /** + Similar to the in-place copy constructor assign(const CImg&). + **/ + template + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy an image into the current image instance \specialization. + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy the content of a display window to the current image instance. + /** + Similar to assign(const CImgDisplay&). + **/ + CImg& operator=(const CImgDisplay& disp) { + disp.snapshot(*this); + return *this; + } + + //! In-place addition operator. + /** + Add specified \c value to all pixels of an image instance. + \param value Value to add. + \note + - Resulting pixel values are casted to fit the pixel type \c T. For instance, adding \c 0.2 to a \c CImg is possible but does nothing indeed. + - Overflow values are treated as with standard C++ numeric types. For instance, + \code + CImg img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'. + img+=1; // Add '1' to each pixels -> Overflow. + // here all pixels of image 'img' are equal to '0'. + \endcode + - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, and use cut() after addition. + \par Example + \code + CImg img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]). + CImg img2(img1); // Construct a float-valued copy of 'img1'. + img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats. + img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint. + img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'. + const CImg img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way. + (img1,img2,img3).display(); + \endcode + \image html ref_operator_plus.jpg + **/ + template + CImg& operator+=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + value); + return *this; + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the specified string \c expression. + \param expression Value string describing the way pixel values are added. + \note + - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance, + instead of assigning them. + **/ + CImg& operator+=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator+="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this+=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the values of the input image \c img. + \param img Input image to add. + \note + - The size of the image instance is never modified. + - It is not mandatory that input image \c img has the same size as the image instance. If less values are available + in \c img, then the values are added cyclically. For instance, adding one WxH scalar image (spectrum() equal to \c 1) to + one WxH color image (spectrum() equal to \c 3) means each color channel will be incremented with the same values at the same + locations. + \par Example + \code + CImg img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) + const CImg img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); // Construct a scalar shading (img2.spectrum()==1). + img1+=img2; // Add shading to each channel of 'img1'. + img1.cut(0,255); // Prevent [0,255] overflow. + (img2,img1).display(); + \endcode + \image html ref_operator_plus1.jpg + **/ + template + CImg& operator+=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this+=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator++() { + cimg_for(*this,ptrd,T) ++*ptrd; + return *this; + } + + //! In-place increment operator (postfix). + /** + Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance. + \note + - Use the prefixed version operator++() if you don't need a copy of the initial (pre-incremented) image instance, since + a useless image copy may be expensive in terms of memory usage. + **/ + CImg operator++(int) { + const CImg copy(*this,false); + ++*this; + return copy; + } + + //! Return a non-shared copy of the image instance. + /** + \note + - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T. + Indeed, the usual copy constructor CImg(const CImg&) returns a shared copy of a shared input image, and it may be + not desirable to work on a regular copy (e.g. for a resize operation) if you have no informations about the shared state + of the input image. + - Writing \c (+img) is equivalent to \c CImg(img,false). + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Addition operator. + /** + Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const t value) const { + return CImg<_cimg_Tt>(*this,false)+=value; + } + + //! Addition operator. + /** + Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator+(const char *const expression) const { + return CImg(*this,false)+=expression; + } + + //! Addition operator. + /** + Similar to operator+=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)+=img; + } + + //! In-place substraction operator. + /** + Similar to operator+=(const t), except that it performs a substraction instead of an addition. + **/ + template + CImg& operator-=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - value); + return *this; + } + + //! In-place substraction operator. + /** + Similar to operator+=(const char*), except that it performs a substraction instead of an addition. + **/ + CImg& operator-=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator-="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this-=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place substraction operator. + /** + Similar to operator+=(const CImg&), except that it performs a substraction instead of an addition. + **/ + template + CImg& operator-=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this-=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator--() { + cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1; + return *this; + } + + //! In-place decrement operator (postfix). + /** + Similar to operator++(int), except that it performs a decrement instead of an increment. + **/ + CImg operator--(int) { + const CImg copy(*this,false); + --*this; + return copy; + } + + //! Replace each pixel by its opposite value. + /** + \note + - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. For instance, + the \c unsigned \c char opposite of \c 1 is \c 255. + \par Example + \code + const CImg + img1("reference.jpg"), // Load a RGB color image. + img2 = -img1; // Compute its opposite (in 'unsigned char'). + (img1,img2).display(); + \endcode + \image html ref_operator_minus.jpg + **/ + CImg operator-() const { + return CImg(_width,_height,_depth,_spectrum,(T)0)-=*this; + } + + //! Substraction operator. + /** + Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const t value) const { + return CImg<_cimg_Tt>(*this,false)-=value; + } + + //! Substraction operator. + /** + Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator-(const char *const expression) const { + return CImg(*this,false)-=expression; + } + + //! Substraction operator. + /** + Similar to operator-=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)-=img; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const t), except that it performs a multiplication instead of an addition. + **/ + template + CImg& operator*=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * value); + return *this; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. + **/ + CImg& operator*=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator*="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + mul(CImg(_width,_height,_depth,_spectrum,expression,true)); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place multiplication operator. + /** + Replace the image instance by the matrix multiplication between the image instance and the specified matrix \c img. + \param img Second operand of the matrix multiplication. + \note + - It does \e not compute a pointwise multiplication between two images. For this purpose, use mul(const CImg&) instead. + - The size of the image instance can be modified by this operator. + \par Example + \code + CImg A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4]. + const CImg X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2]. + A*=X; // Assign matrix multiplication A*X to 'A'. + // 'A' is now a 1x2 vector whose values are [5;11]. + \endcode + **/ + template + CImg& operator*=(const CImg& img) { + return ((*this)*img).move_to(*this); + } + + //! Multiplication operator. + /** + Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const t value) const { + return CImg<_cimg_Tt>(*this,false)*=value; + } + + //! Multiplication operator. + /** + Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator*(const char *const expression) const { + return CImg(*this,false)*=expression; + } + + //! Multiplication operator. + /** + Similar to operator*=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const CImg& img) const { + if (_width!=img._height || _depth!=1 || _spectrum!=1) + throw CImgArgumentException(_cimg_instance + "operator*(): Invalid multiplication of instance by specified matrix (%u,%u,%u,%u,%p)", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + CImg<_cimg_Tt> res(img._width,_height); + _cimg_Ttdouble value; +#ifdef cimg_use_openmp +#pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(value) + cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value; } +#else + _cimg_Tt *ptrd = res._data; + cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value; } +#endif + return res; + } + + //! In-place division operator. + /** + Similar to operator+=(const t), except that it performs a division instead of an addition. + **/ + template + CImg& operator/=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / value); + return *this; + } + + //! In-place division operator. + /** + Similar to operator+=(const char*), except that it performs a division instead of an addition. + **/ + CImg& operator/=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator/="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + div(CImg(_width,_height,_depth,_spectrum,expression,true)); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place division operator. + /** + Replace the image instance by the (right) matrix division between the image instance and the specified matrix \c img. + \param img Second operand of the matrix division. + \note + - It does \e not compute a pointwise division between two images. For this purpose, use div(const CImg&) instead. + - It returns the matrix operation \c A*inverse(img). + - The size of the image instance can be modified by this operator. + **/ + template + CImg& operator/=(const CImg& img) { + return (*this*img.get_invert()).move_to(*this); + } + + //! Division operator. + /** + Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const t value) const { + return CImg<_cimg_Tt>(*this,false)/=value; + } + + //! Division operator. + /** + Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator/(const char *const expression) const { + return CImg(*this,false)/=expression; + } + + //! Division operator. + /** + Similar to operator/=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const CImg& img) const { + return (*this)*img.get_invert(); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const t), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value); + return *this; + } + + //! In-place modulo operator. + /** + Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. + **/ + CImg& operator%=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator%="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this%=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place modulo operator. + /** + Similar to operator+=(const CImg&), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this%=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> operator%(const t value) const { + return CImg<_cimg_Tt>(*this,false)%=value; + } + + //! Modulo operator. + /** + Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator%(const char *const expression) const { + return CImg(*this,false)%=expression; + } + + //! Modulo operator. + /** + Similar to operator%=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator%(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)%=img; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value); + return *this; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. + **/ + CImg& operator&=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator&="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this&=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this&=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator&(const t value) const { + return (+*this)&=value; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator&(const char *const expression) const { + return (+*this)&=expression; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator&(const CImg& img) const { + return (+*this)&=img; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value); + return *this; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. + **/ + CImg& operator|=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator|="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this|=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this|=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator|(const t value) const { + return (+*this)|=value; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator|(const char *const expression) const { + return (+*this)|=expression; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator|(const CImg& img) const { + return (+*this)|=img; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead. + **/ + template + CImg& operator^=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value); + return *this; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. + **/ + CImg& operator^=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator^="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this^=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg&) instead. + **/ + template + CImg& operator^=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator^(const t value) const { + return (+*this)^=value; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator^(const char *const expression) const { + return (+*this)^=expression; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator^(const CImg& img) const { + return (+*this)^=img; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value); + return *this; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. + **/ + CImg& operator<<=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this<<=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator<<(const t value) const { + return (+*this)<<=value; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator<<(const char *const expression) const { + return (+*this)<<=expression; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator<<(const CImg& img) const { + return (+*this)<<=img; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const t value) { + cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value); + return *this; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. + **/ + CImg& operator>>=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<="); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + cimg::exception_mode() = omode; + *this>>=CImg(_width,_height,_depth,_spectrum,expression,true); + } + cimg::exception_mode() = omode; + return *this; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); + for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); + } + return *this; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const t value) const { + return (+*this)>>=value; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator>>(const char *const expression) const { + return (+*this)>>=expression; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const CImg& img) const { + return (+*this)>>=img; + } + + //! Bitwise inversion operator. + /** + Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value. + **/ + CImg operator~() const { + CImg res(_width,_height,_depth,_spectrum); + const T *ptrs = _data; + cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(ptrs++); *ptrd = (T)~value; } + return res; + } + + //! Test if all pixels of an image have the same value. + /** + Return \c true is all pixels of the image instance are equal to the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator==(const t value) const { + if (is_empty()) return false; + typedef _cimg_Tt Tt; + bool is_equal = true; + for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {} + return is_equal; + } + + //! Test if all pixel values of an image follow a specified expression. + /** + Return \c true is all pixels of the image instance are equal to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator==(const char *const expression) const { + if (is_empty()) return !*expression; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + bool is_equal = true; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<="); + const T *ptrs = _data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs--)==mp.eval(x,y,z,c)); } + else cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp.eval(x,y,z,c)); } + } catch (CImgException&) { + cimg::exception_mode() = omode; + is_equal = (*this==CImg(_width,_height,_depth,_spectrum,expression,true)); + } + cimg::exception_mode() = omode; + return is_equal; + } + + //! Test if two images have the same size and values. + /** + Return \c true if the image instance and the input image \c img have the same dimensions and pixel values, and \c false otherwise. + \param img Input image to compare with. + \note + - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() to return \c true. + Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different pixel types \c T and \c t. + \par Example + \code + const CImg img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values). + const CImg img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values). + if (img1==img2) { // Test succeeds, image dimensions and values are the same. + std::printf("'img1' and 'img2' have same dimensions and values."); + } + \endcode + **/ + template + bool operator==(const CImg& img) const { + typedef _cimg_Tt Tt; + const unsigned long siz = size(); + bool is_equal = true; + if (siz!=img.size()) return false; + t *ptrs = img._data + siz; + for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {} + return is_equal; + } + + //! Test if pixels of an image are all different from a value. + /** + Return \c true is all pixels of the image instance are different than the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator!=(const t value) const { + return !((*this)==value); + } + + //! Test if all pixel values of an image are different from a specified expression. + /** + Return \c true is all pixels of the image instance are different to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator!=(const char *const expression) const { + return !((*this)==expression); + } + + //! Test if two images have different sizes or values. + /** + Return \c true if the image instance and the input image \c img have different dimensions or pixel values, and \c false otherwise. + \param img Input image to compare with. + \note + - Writing \c img1!=img2 is equivalent to \c !(img1==img2). + **/ + template + bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Construct an image list from two images. + /** + Return a new list of image (\c CImgList instance) containing exactly two elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image \c img, at position [\c 1]. + + \param img Input image that will be the second image of the resulting list. + \note + - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow in practice (see warning below). + - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are + inserted as new non-shared copies in the resulting list. + - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary. + \warning + - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list. + This may become very expensive in terms of speed and used memory. You should avoid using this technique to + build a new CImgList instance from several images, if you are seeking for performance. + Fast insertions of images in an image list are possible with CImgList::insert(const CImg&,unsigned int,bool) or + move_to(CImgList&,unsigned int). + \par Example + \code + const CImg + img1("reference.jpg"), + img2 = img1.get_mirror('x'), + img3 = img2.get_blur(5); + const CImgList list = (img1,img2); // Create list of two elements from 'img1' and 'img2'. + (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3'. + \endcode + \image html ref_operator_comma.jpg + **/ + template + CImgList<_cimg_Tt> operator,(const CImg& img) const { + return CImgList<_cimg_Tt>(*this,img); + } + + //! Construct an image list from image instance and an input image list. + /** + Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()]. + + \param list Input image list that will be appended to the image instance. + \note + - Similar to operator,(const CImg&) const, except that it takes an image list as an argument. + **/ + template + CImgList<_cimg_Tt> operator,(const CImgList& list) const { + return CImgList<_cimg_Tt>(list,false).insert(*this,0); + } + + //! Split image along specified axis. + /** + Return a new list of images (\c CImgList instance) containing the splitted components + of the instance image along the specified axis. + \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c') + \note + - Similar to get_split(char,int) const, with default second argument. + \par Example + \code + const CImg img("reference.jpg"); // Load a RGB color image. + const CImgList list = (img<'c'); // Get a list of its three R,G,B channels. + (img,list).display(); + \endcode + \image html ref_operator_less.jpg + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the number of image columns. + /** + Return the image width, i.e. the image dimension along the X-axis. + \note + - The width() of an empty image is equal to \c 0. + - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations. + - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by (*this)._width. + **/ + int width() const { + return (int)_width; + } + + //! Return the number of image rows. + /** + Return the image height, i.e. the image dimension along the Y-axis. + \note + - The height() of an empty image is equal to \c 0. + - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by (*this)._height. + **/ + int height() const { + return (int)_height; + } + + //! Return the number of image slices. + /** + Return the image depth, i.e. the image dimension along the Z-axis. + \note + - The depth() of an empty image is equal to \c 0. + - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image + is said to be \e volumetric. + - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by (*this)._depth. + **/ + int depth() const { + return (int)_depth; + } + + //! Return the number of image channels. + /** + Return the number of image channels, i.e. the image dimension along the C-axis. + \note + - The spectrum() of an empty image is equal to \c 0. + - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 for RGB-coded color images, and to + \c 4 for RGBA-coded color images (with alpha-channel). The number of channels of an image instance + is not limited. The meaning of the pixel values is not linked up to the number of channels + (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image). + - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by (*this)._spectrum. + **/ + int spectrum() const { + return (int)_spectrum; + } + + //! Return the total number of pixel values. + /** + Return width()*\ref height()*\ref depth()*\ref spectrum(), + i.e. the total number of values of type \c T in the pixel buffer of the image instance. + \note + - The size() of an empty image is equal to \c 0. + - The allocated memory size for a pixel buffer of a non-shared \c CImg instance is equal to size()*sizeof(T). + \par Example + \code + const CImg img(100,100,1,3); // Construct new 100x100 color image. + if (img.size()==30000) // Test succeeds. + std::printf("Pixel buffer uses %lu bytes", + img.size()*sizeof(float)); + \endcode + **/ + unsigned long size() const { + return (unsigned long)_width*_height*_depth*_spectrum; + } + + //! Return a pointer to the first pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance, + whether the instance is \c const or not. + \note + - The data() of an empty image is equal to \c 0 (null pointer). + - The allocated pixel buffer for the image instance starts from \c data() + and goes to data()+\ref size()-1 (included). + - To get the pointer to one particular location of the pixel buffer, use data(unsigned int,unsigned int,unsigned int,unsigned int) instead. + **/ + T* data() { + return _data; + } + + //! Return a pointer to the first pixel value \const. + const T* data() const { + return _data; + } + + //! Return a pointer to a located pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer of the image instance, + whether the instance is \c const or not. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)). Thus, this method has the same properties as + operator()(unsigned int,unsigned int,unsigned int,unsigned int). + **/ +#if cimg_verbosity>=3 + T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + const unsigned long off = (unsigned long)offset(x,y,z,c); + if (off>=size()) + cimg::warn(_cimg_instance + "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", + cimg_instance, + x,y,z,c,off); + return _data + off; + } + + //! Return a pointer to a located pixel value \const. + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->data(x,y,z,c); + } +#else + T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + return _data + x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth; + } + + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return _data + x + y*_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth; + } +#endif + + //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)) - img.data(). + Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + \par Example + \code + const CImg img(100,100,1,3); // Define a 100x100 RGB-color image. + const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10). + const float val = img[off]; // Get the blue value of this pixel. + \endcode + **/ + long offset(const int x, const int y=0, const int z=0, const int c=0) const { + return x + y*(long)_width + z*(long)_width*_height + c*(long)_width*_height*_depth; + } + + //! Return a CImg::iterator pointing to the first pixel value. + /** + \note + - Equivalent to data(). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + iterator begin() { + return _data; + } + + //! Return a CImg::iterator pointing to the first value of the pixel buffer \const. + const_iterator begin() const { + return _data; + } + + //! Return a CImg::iterator pointing next to the last pixel value. + /** + \note + - Writing \c img.end() is equivalent to img.data() + img.size(). + - It has been mainly defined for compatibility with STL naming conventions. + \warning + - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. Trying + to read or write the content of the returned iterator will probably result in a crash. Use it mainly as an + strict upper bound for a CImg::iterator. + \par Example + \code + CImg img(100,100,1,3); // Define a 100x100 RGB color image. + for (CImg::iterator it = img.begin(); it::iterator pointing next to the last pixel value \const. + const_iterator end() const { + return _data + size(); + } + + //! Return a reference to the first pixel value. + /** + \note + - Writing \c img.front() is equivalent to img[0], or img(0,0,0,0). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& front() { + return *_data; + } + + //! Return a reference to the first pixel value \const. + const T& front() const { + return *_data; + } + + //! Return a reference to the last pixel value. + /** + \note + - Writing \c img.end() is equivalent to img[img.size()-1], or + img(img.width()-1,img.height()-1,img.depth()-1,img.spectrum()-1). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& back() { + return *(_data + size() - 1); + } + + //! Return a reference to the last pixel value \const. + const T& back() const { + return *(_data + size() - 1); + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to a specified default value in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note + - Writing \c img.at(offset,out_value) is similar to img[offset], except that if \c offset + is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value + is safely returned instead. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + **/ + T& at(const int offset, const T out_value) { + return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const. + T at(const int offset, const T out_value) const { + return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to the nearest pixel location in the image instance in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \note + - Similar to at(int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified offset, i.e. + - If \c offset<0, then \c img[0] is returned. + - If \c offset>=img.size(), then \c img[img.size()-1] is returned. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int). + **/ + T& at(const int offset) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + T& _at(const int offset) { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const. + T at(const int offset) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + T _at(const int offset) const { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset]; + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to a specified default value in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value \c out_value. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y, const int z, const int c, const T out_value) { + return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const. + T atX(const int x, const int y, const int z, const int c, const T out_value) const { + return (x<0 || x>=width())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified X-coordinate. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + T& _atX(const int x, const int y=0, const int z=0, const int c=0) { + return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const. + T atX(const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + T _atX(const int x, const int y=0, const int z=0, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates. + **/ + T& atXY(const int x, const int y, const int z, const int c, const T out_value) { + return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const. + T atXY(const int x, const int y, const int z, const int c, const T out_value) const { + return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXY(int,int,int,int). + **/ + T& atXY(const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + T& _atXY(const int x, const int y, const int z=0, const int c=0) { + return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const. + T atXY(const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + T _atXY(const int x, const int y, const int z=0, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X,Y and Z-coordinates. + **/ + T& atXYZ(const int x, const int y, const int z, const int c, const T out_value) { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const. + T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZ(int,int,int,int). + **/ + T& atXYZ(const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + T& _atXYZ(const int x, const int y, const int z, const int c=0) { + return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y), + z<0?0:(z>=depth()?depth()-1:z),c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const. + T atXYZ(const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + T _atXYZ(const int x, const int y, const int z, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y), + z<0?0:(z>=depth()?depth()-1:z),c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all X,Y,Z and C-coordinates. + **/ + T& atXYZC(const int x, const int y, const int z, const int c, const T out_value) { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions \const. + T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZC(int,int,int,int). + **/ + T& atXYZC(const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + T& _atXYZC(const int x, const int y, const int z, const int c) { + return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y), + z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c)); + } + + //! Access to a pixel value, using Neumann boundary conditions \const. + T atXYZC(const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + T _atXYZC(const int x, const int y, const int z, const int c) const { + return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y), + z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c)); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by a linear interpolation along the X-axis, + if corresponding coordinates are not integers. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) 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,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value); + return Ic + dx*(In-Ic); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified X-coordinate. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX(): Empty instance.", + cimg_instance); + + return _linear_atX(fx,y,z,c); + } + + Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=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,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In-Ic); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking + are achieved both for X and Y-coordinates. + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) 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,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), + Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXY(float,float,int,int). + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY(): Empty instance.", + cimg_instance); + + return _linear_atXY(fx,fy,z,c); + } + + Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=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,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking + are achieved both for X,Y and Z-coordinates. + **/ + Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) 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,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), + Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), + Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), + Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value); + 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); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXYZ(float,float,float,int). + **/ + Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ(): Empty instance.", + cimg_instance); + + return _linear_atXYZ(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=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,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + 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); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z and C-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking + are achieved for all X,Y,Z and C-coordinates. + **/ + Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) 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, + c = (int)fc - (fc>=0?0:1), nc = c + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z, + dc = fc - c; + const Tfloat + Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value), + Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value), + Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value), + Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value), + Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value), + Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value), + Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value), + Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dc*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dc*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dc*(Icccc+Iccnn-Iccnc-Icccn)) + + dc*(Icccn-Icccc); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved for all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXYZC(float,float,float,float). + **/ + Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC(): Empty instance.", + cimg_instance); + + return _linear_atXYZC(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=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), + nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = dx>0?x+1:x, + ny = dy>0?y+1:y, + nz = dz>0?z+1:z, + nc = dc>0?c+1:c; + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dc*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dc*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dc*(Icccc+Iccnn-Iccnc-Icccn)) + + dc*(Icccn-Icccc); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + \param fx d X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is approximated by a + \e cubic interpolation along the X-axis. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) 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,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value), + In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value); + return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atX(fx,y,z,c,out_value); + return valmax_value?max_value:val; + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is approximated by a cubic interpolation along the X-axis. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX(): Empty instance.", + cimg_instance); + return _cubic_atX(fx,y,z,c); + } + + Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=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>=width()?width()-1:x+2; + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atX(fx,y,z,c); + return valmax_value?max_value:val; + } + + Tfloat _cubic_atX(const float fx, const int y, const int z, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = _cubic_atX(fx,y,z,c); + return valmax_value?max_value:val; + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X and Y-coordinates. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) 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, dy = fy - y; + const Tfloat + Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value), + Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)), + Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value), + Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)), + Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value), + In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)), + Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value), + Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa)); + return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atXY(fx,fy,z,c,out_value); + return valmax_value?max_value:val; + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved for both X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atXY(float,float,int,int). + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY(): Empty instance.", + cimg_instance); + return _cubic_atXY(fx,fy,z,c); + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=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, dy = nfy - y; + const int + px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2, + py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2; + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa)); + return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atXY(fx,fy,z,c); + return valmax_value?max_value:val; + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = _cubic_atXY(fx,fy,z,c); + return valmax_value?max_value:val; + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) 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, + z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2; + const float dx = fx - x, dy = fy - y, dz = fz - z; + const Tfloat + Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value), + Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value), + Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)), + Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value), + Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value), + Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)), + Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value), + Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value), + Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)), + Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value), + Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value), + Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)), + Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value), + Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value), + Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)), + Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value), + Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value), + Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)), + Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), + Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value), + Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)), + Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value), + Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value), + Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)), + Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value), + Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value), + Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)), + Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value), + Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value), + Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)), + Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), + Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value), + Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)), + Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value), + Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value), + Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)), + In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)), + Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value), + Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value), + Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)), + Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value), + Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value), + Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)), + Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value), + Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value), + Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)), + Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value), + Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value), + Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa)); + return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value); + return valmax_value?max_value:val; + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ(): Empty instance.", + cimg_instance); + return _cubic_atXYZ(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=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 int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2, + py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2, + pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2; + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)), + In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa)); + return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia)); + } + + //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and maximum of the returned value. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = cubic_atXYZ(fx,fy,fz,c); + return valmax_value?max_value:val; + } + + Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c, + const Tfloat min_value, const Tfloat max_value) const { + const Tfloat val = _cubic_atXYZ(fx,fy,fz,c); + return valmax_value?max_value:val; + } + + //! Set pixel value, using linear interpolation for the X and Y-coordinates. + /** + Set pixel value at specified coordinates (\c fx,\c fy,\c z,\c c) in the image instance, in a way that the value is spread + amongst several neighbors if the pixel coordinates are indeed float-valued. + \param value Pixel value to set. + \param fx X-coordinate of the pixel value (float-valued). + \param fy Y-coordinate of the pixel value (float-valued). + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image pixel(s). + \return A reference to the current image instance. + \note + - If specified coordinates are outside image bounds, no operations are performed. + **/ + CImg& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0, + const bool is_added=false) { + 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=0 && c=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0, + const bool is_added=false) { + 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 (c>=0 && c=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx image whose buffer data() is a \c char* string describing the list of all pixel values + of the image instance (written in base 10), separated by specified \c separator character. + \param separator A \c char character which specifies the separator between values in the returned C-string. + \param max_size Maximum size of the returned image. + \note + - The returned image is never empty. + - For an empty image instance, the returned string is "". + - If \c max_size is equal to \c 0, there are no limits on the size of the returned string. + - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off + and terminated by character \c '\0'. In that case, the returned image size is max_size + 1. + **/ + CImg value_string(const char separator=',', const unsigned int max_size=0) const { + if (is_empty()) return CImg::string(""); + CImgList items; + char s_item[256] = { 0 }; + const T *ptrs = _data; + unsigned int string_size = 0; + for (unsigned long off = 0, siz = (unsigned int)size(); off::format(),cimg::type::format(*(ptrs++))); + CImg item(s_item,printed_size); + item[printed_size-1] = separator; + item.move_to(items); + if (max_size) string_size+=printed_size; + } + CImg res; + (items>'x').move_to(res); + if (max_size && res._width>max_size) res.crop(0,max_size); + res.back() = 0; + return res; + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Test shared state of the pixel buffer. + /** + Return \c true if image instance has a shared memory buffer, and \c false otherwise. + \note + - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. + - Most of the time, a \c CImg image instance will \e not be shared. + - A shared image can only be obtained by a limited set of constructors and methods (see list below). + **/ + bool is_shared() const { + return _is_shared; + } + + //! Test if image instance is empty. + /** + Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions \c 0 x \c 0 x \c 0 x \c 0 + and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise. + **/ + bool is_empty() const { + return !(_data && _width && _height && _depth && _spectrum); + } + + //! Test if image instance contains a 'inf' value. + /** + Return \c true, if image instance contains a 'inf' value, and \c false otherwise. + **/ + bool is_inf() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_inf((float)*p)) return true; + return false; + } + + //! Test if image instance contains a 'nan' value. + /** + Return \c true, if image instance contains a 'nan' value, and \c false otherwise. + **/ + bool is_nan() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_nan((float)*p)) return true; + return false; + } + + //! Test if image width is equal to specified value. + bool is_sameX(const unsigned int size_x) const { + return _width==size_x; + } + + //! Test if image width is equal to specified value. + template + bool is_sameX(const CImg& img) const { + return is_sameX(img._width); + } + + //! Test if image width is equal to specified value. + bool is_sameX(const CImgDisplay& disp) const { + return is_sameX(disp._width); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const unsigned int size_y) const { + return _height==size_y; + } + + //! Test if image height is equal to specified value. + template + bool is_sameY(const CImg& img) const { + return is_sameY(img._height); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const CImgDisplay& disp) const { + return is_sameY(disp._height); + } + + //! Test if image depth is equal to specified value. + bool is_sameZ(const unsigned int size_z) const { + return _depth==size_z; + } + + //! Test if image depth is equal to specified value. + template + bool is_sameZ(const CImg& img) const { + return is_sameZ(img._depth); + } + + //! Test if image spectrum is equal to specified value. + bool is_sameC(const unsigned int size_c) const { + return _spectrum==size_c; + } + + //! Test if image spectrum is equal to specified value. + template + bool is_sameC(const CImg& img) const { + return is_sameC(img._spectrum); + } + + //! Test if image width and height are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified. + **/ + bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const { + return _width==size_x && _height==size_y; + } + + //! Test if image width and height are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameY(const CImg&) const are both verified. + **/ + template + bool is_sameXY(const CImg& img) const { + return is_sameXY(img._width,img._height); + } + + //! Test if image width and height are the same as that of an existing display window. + /** + Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified. + **/ + bool is_sameXY(const CImgDisplay& disp) const { + return is_sameXY(disp._width,disp._height); + } + + //! Test if image width and depth are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const { + return _width==size_x && _depth==size_z; + } + + //! Test if image width and depth are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXZ(const CImg& img) const { + return is_sameXZ(img._width,img._depth); + } + + //! Test if image width and spectrum are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const { + return _width==size_x && _spectrum==size_c; + } + + //! Test if image width and spectrum are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXC(const CImg& img) const { + return is_sameXC(img._width,img._spectrum); + } + + //! Test if image height and depth are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const { + return _height==size_y && _depth==size_z; + } + + //! Test if image height and depth are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameYZ(const CImg& img) const { + return is_sameYZ(img._height,img._depth); + } + + //! Test if image height and spectrum are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const { + return _height==size_y && _spectrum==size_c; + } + + //! Test if image height and spectrum are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYC(const CImg& img) const { + return is_sameYC(img._height,img._spectrum); + } + + //! Test if image depth and spectrum are equal to specified values. + /** + Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const { + return _depth==size_z && _spectrum==size_c; + } + + //! Test if image depth and spectrum are the same as that of another image. + /** + Test if is_sameZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameZC(const CImg& img) const { + return is_sameZC(img._depth,img._spectrum); + } + + //! Test if image width, height and depth are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const { + return is_sameXY(size_x,size_y) && _depth==size_z; + } + + //! Test if image width, height and depth are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXYZ(const CImg& img) const { + return is_sameXYZ(img._width,img._height,img._depth); + } + + //! Test if image width, height and spectrum are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const { + return is_sameXY(size_x,size_y) && _spectrum==size_c; + } + + //! Test if image width, height and spectrum are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYC(const CImg& img) const { + return is_sameXYC(img._width,img._height,img._spectrum); + } + + //! Test if image width, depth and spectrum are equal to specified values. + /** + Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const { + return is_sameXZ(size_x,size_z) && _spectrum==size_c; + } + + //! Test if image width, depth and spectrum are the same as that of another image. + /** + Test if is_sameXZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXZC(const CImg& img) const { + return is_sameXZC(img._width,img._depth,img._spectrum); + } + + //! Test if image height, depth and spectrum are equal to specified values. + /** + Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { + return is_sameYZ(size_y,size_z) && _spectrum==size_c; + } + + //! Test if image height, depth and spectrum are the same as that of another image. + /** + Test if is_sameYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYZC(const CImg& img) const { + return is_sameYZC(img._height,img._depth,img._spectrum); + } + + //! Test if image width, height, depth and spectrum are equal to specified values. + /** + Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { + return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c; + } + + //! Test if image width, height, depth and spectrum are the same as that of another image. + /** + Test if is_sameXYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYZC(const CImg& img) const { + return is_sameXYZC(img._width,img._height,img._depth,img._spectrum); + } + + //! Test if specified coordinates are inside image bounds. + /** + Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, and \c false otherwise. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Return \c true only if all these conditions are verified: + - The image instance is \e not empty. + - 0<=x<=\ref width()-1. + - 0<=y<=\ref height()-1. + - 0<=z<=\ref depth()-1. + - 0<=c<=\ref spectrum()-1. + **/ + bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { + return !is_empty() && x>=0 && x=0 && y=0 && z=0 && c img(100,100,1,3); // Construct a 100x100 RGB color image. + const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0). + unsigned int x,y,z,c; + if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates. + std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n", + offset,x,y,z,c); + } + \endcode + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { + const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + 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 nc = off/whd; + off%=whd; + 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; c = (t)nc; + return true; + } + + //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z) const { + const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false; + unsigned long off = ((unsigned long)(ppixel - _data))%whd; + 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; + } + + //! Test if pixel value is inside image bounds and get its X and Y-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y) const { + const unsigned long wh = (unsigned long)_width*_height, siz = wh*_depth*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false; + unsigned long off = ((unsigned int)(ppixel - _data))%wh; + const unsigned long ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; + return true; + } + + //! Test if pixel value is inside image bounds and get its X-coordinate. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set. + **/ + template + 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; + } + + //! Test if pixel value is inside image bounds. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set. + **/ + bool contains(const T& pixel) const { + const T *const ppixel = &pixel; + return !is_empty() && ppixel>=_data && ppixel<_data + size(); + } + + //! Test if pixel buffers of instance and input images overlap. + /** + Return \c true, if pixel buffers attached to image instance and input image \c img overlap, and \c false otherwise. + \param img Input image to compare with. + \note + - Buffer overlapping may happen when manipulating \e shared images. + - If two image buffers overlap, operating on one of the image will probably modify the other one. + - Most of the time, \c CImg instances are \e non-shared and do not overlap between each others. + \par Example + \code + const CImg + img1("reference.jpg"), // Load RGB-color image. + img2 = img1.get_shared_channel(1); // Get shared version of the green channel. + if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps. + std::printf("Buffers overlap!\n"); + } + \endcode + **/ + template + bool is_overlapped(const CImg& img) const { + const unsigned long csiz = size(), isiz = img.size(); + return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); + } + + //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object. + /** + Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a + valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image. + \param primitives List of primitives of the 3d object. + \param colors List of colors of the 3d object. + \param opacities List (or image) of opacities of the 3d object. + \param is_full_check Tells if full checking of the 3d object must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of + each 3d object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + template + bool is_object3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool is_full_check=true, + char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check consistency for the particular case of an empty 3d object. + if (is_empty()) { + if (primitives || colors || opacities) { + if (error_message) std::sprintf(error_message, + "3d object has no vertices but %u primitives, %u colors and %lu opacities", + primitives._width,colors._width,(unsigned long)opacities.size()); + return false; + } + return true; + } + + // Check consistency of vertices. + if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions. + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)", + _width,primitives._width,_width,_height,_depth,_spectrum); + return false; + } + if (colors._width>primitives._width+1) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) defines %u colors", + _width,primitives._width,colors._width); + return false; + } + if (opacities.size()>primitives._width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) defines %lu opacities", + _width,primitives._width,(unsigned long)opacities.size()); + return false; + } + if (!is_full_check) return true; + + // Check consistency of primitives. + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + const unsigned long psiz = primitive.size(); + switch (psiz) { + case 1 : { // Point. + const unsigned int i0 = (unsigned int)primitive(0); + if (i0>=_width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u", + _width,primitives._width,i0,l); + return false; + } + } break; + case 5 : { // Sphere. + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 2 : // Segment. + case 6 : { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 3 : // Triangle. + case 9 : { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + if (i0>=_width || i1>=_width || i2>=_width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u", + _width,primitives._width,i0,i1,i2,l); + return false; + } + } break; + case 4 : // Quadrangle. + 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); + if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { + if (error_message) std::sprintf(error_message, + "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u", + _width,primitives._width,i0,i1,i2,i3,l); + return false; + } + } break; + default : + if (error_message) std::sprintf(error_message, + "3d object has invalid primitive %u of size %u", + l,(unsigned int)psiz); + return false; + } + } + + // Check consistency of colors. + cimglist_for(colors,c) { + const CImg& color = colors[c]; + if (!color) { + if (error_message) std::sprintf(error_message, + "3d object has empty color for primitive %u", + c); + return false; + } + } + + // Check consistency of light texture. + if (colors._width>primitives._width) { + const CImg &light = colors.back(); + if (!light || light._depth>1) { + if (error_message) std::sprintf(error_message, + "3d object has invalid light texture (%u,%u,%u,%u)", + light._width,light._height,light._depth,light._spectrum); + return false; + } + } + + return true; + } + + //! Test if image instance represents a valid serialization of a 3d object. + /** + Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise. + \param is_full_check Tells if full checking of the instance must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of + each 3d object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + bool is_CImg3d(const bool is_full_check=true, char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check instance dimension and header. + if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { + if (error_message) std::sprintf(error_message, + "CImg3d has invalid dimensions (%u,%u,%u,%u)", + _width,_height,_depth,_spectrum); + return false; + } + const T *ptrs = _data, *const ptre = end(); + if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || + !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { + if (error_message) std::sprintf(error_message, + "CImg3d header not found"); + return false; + } + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + + // Check consistency of vertex data. + if (!nb_points) { + if (nb_primitives) { + if (error_message) std::sprintf(error_message, + "CImg3d has no vertices but %u primitives", + nb_primitives); + return false; + } + if (ptrs!=ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) is empty but contains %u byte%s more than expected", + nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":""); + return false; + } + return true; + } + ptrs+=3*nb_points; + if (ptrs>ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has incomplete vertex data", + nb_points,nb_primitives); + return false; + } + if (!is_full_check) return true; + + // Check consistency of primitive data. + if (ptrs==ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has no primitive data", + nb_points,nb_primitives); + return false; + } + for (unsigned int p = 0; p=nb_points) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u", + nb_points,nb_primitives,i0,p); + return false; + } + } break; + case 5 : { // Sphere. + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + ptrs+=3; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 2 : case 6 : { // Segment. + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==6) ptrs+=4; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 3 : case 9 : { // Triangle. + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==9) ptrs+=6; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u", + nb_points,nb_primitives,i0,i1,i2,p); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle. + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)), + i3 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==12) ptrs+=8; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u", + nb_points,nb_primitives,i0,i1,i2,i3,p); + return false; + } + } break; + default : + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has invalid primitive %u of size %u", + nb_points,nb_primitives,p,nb_inds); + return false; + } + if (ptrs>ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has incomplete primitive data for primitive %u", + nb_points,nb_primitives,p); + return false; + } + } + + // Check consistency of color data. + if (ptrs==ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has no color/texture data", + nb_points,nb_primitives); + return false; + } + for (unsigned int c = 0; c=c) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u", + nb_points,nb_primitives,w,c); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has incomplete color/texture data for primitive %u", + nb_points,nb_primitives,c); + return false; + } + } + + // Check consistency of opacity data. + if (ptrs==ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has no opacity data", + nb_points,nb_primitives); + return false; + } + for (unsigned int o = 0; o=o) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u", + nb_points,nb_primitives,w,o); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) std::sprintf(error_message, + "CImg3d (%u,%u) has incomplete opacity data for primitive %u", + nb_points,nb_primitives,o); + return false; + } + } + + // Check end of data. + if (ptrs1?"s":""); + return false; + } + return true; + } + + static bool _is_CImg3d(const T val, const char c) { + return val>=(T)c && val<(T)(c+1); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + // Define the math formula parser/compiler and evaluator. + struct _cimg_math_parser { + CImgList label; + CImgList code; + CImg level, opcode; + const CImg* p_code; + CImg mem; + CImg expr; + const CImg& reference; + CImg reference_stats; + unsigned int mempos, result; + const char *const calling_function; + typedef double (_cimg_math_parser::*mp_func)(); + const mp_func *mp_funcs; + +#define _cimg_mp_return(x) { *se = saved_char; return x; } +#define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op)); +#define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1)); +#define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); } +#define _cimg_mp_opcode3(op,i1,i2,i3) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); } +#define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \ + _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); } + + // Constructor - Destructor. + _cimg_math_parser(const CImg& img, const char *const expression, const char *const funcname=0): + reference(img),calling_function(funcname?funcname:"cimg_math_parser") { + unsigned int l = 0; + if (expression) { + l = (unsigned int)std::strlen(expression); + expr.assign(expression,l+1); + if (*expr._data) { + char *d = expr._data; + for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s; + l = (unsigned int)(d - expr._data); + } + } + if (!l) throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Empty specified expression.", + pixel_type(),calling_function); + + int lv = 0; // Count parenthesis level of expression. + level.assign(l); + unsigned int *pd = level._data; + for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv); + if (lv!=0) { + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Unbalanced parentheses in specified expression '%s'.", + pixel_type(),calling_function, + expr._data); + } + // Init constant values. + mem.assign(512); + label.assign(512); + mem[0] = 0; + mem[1] = 1; + mem[2] = 2; + mem[3] = (double)reference._width; + mem[4] = (double)reference._height; + mem[5] = (double)reference._depth; + mem[6] = (double)reference._spectrum; + mem[7] = cimg::PI; + mem[8] = std::exp(1.0); // Then [9] = x, [10] = y, [11] = z, [12] = c + mempos = 13; + result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes. + } + + // Insert code instructions. + unsigned int opcode0(const char op) { + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(op,pos).move_to(code); + return pos; + } + + unsigned int opcode1(const char op, const unsigned int arg1) { + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(op,pos,arg1).move_to(code); + return pos; + } + + unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) { + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(op,pos,arg1,arg2).move_to(code); + return pos; + } + + unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(op,pos,arg1,arg2,arg3).move_to(code); + return pos; + } + + unsigned int opcode6(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + return pos; + } + + // Compilation procedure. + unsigned int compile(char *const ss, char *const se) { + if (!ss || se<=ss || !*ss) { + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Missing item in specified expression '%s'.", + pixel_type(),calling_function, + expr._data); + } + char + *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4, + *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4, + *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7; + const char saved_char = *se; *se = 0; + const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1; + if (*se1==';') return compile(ss,se1); + + // Look for a single value, variable or variable assignment. + char end = 0, sep = 0; double val = 0; + const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end); + if (nb==1) { + if (val==0) _cimg_mp_return(0); + if (val==1) _cimg_mp_return(1); + if (val==2) _cimg_mp_return(2); + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + mem[pos] = val; + _cimg_mp_return(pos); + } + if (nb==2 && sep=='%') { + if (val==0) _cimg_mp_return(0); + if (val==100) _cimg_mp_return(1); + if (val==200) _cimg_mp_return(2); + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + mem[pos] = val/100; + _cimg_mp_return(pos); + } + if (ss1==se) switch (*ss) { + case 'w' : _cimg_mp_return(3); case 'h' : _cimg_mp_return(4); case 'd' : _cimg_mp_return(5); case 's' : _cimg_mp_return(6); + case 'x' : _cimg_mp_return(9); case 'y' : _cimg_mp_return(10); case 'z' : _cimg_mp_return(11); case 'c' : _cimg_mp_return(12); + case 'e' : _cimg_mp_return(8); + case 'u' : case '?' : _cimg_mp_opcode2(0,0,1); + case 'g' : _cimg_mp_opcode0(1); + case 'i' : _cimg_mp_opcode0(2); + } + if (ss1==se1) { + if (*ss=='p' && *ss1=='i') _cimg_mp_return(7); // pi + if (*ss=='i') { + if (*ss1=='m') _cimg_mp_opcode0(57); // im + if (*ss1=='M') _cimg_mp_opcode0(58); // iM + if (*ss1=='a') _cimg_mp_opcode0(59); // ia + if (*ss1=='v') _cimg_mp_opcode0(60); // iv + } + if (*ss1=='m') { + if (*ss=='x') _cimg_mp_opcode0(61); // xm + if (*ss=='y') _cimg_mp_opcode0(62); // ym + if (*ss=='z') _cimg_mp_opcode0(63); // zm + if (*ss=='c') _cimg_mp_opcode0(64); // cm + } + if (*ss1=='M') { + if (*ss=='x') _cimg_mp_opcode0(65); // xM + if (*ss=='y') _cimg_mp_opcode0(66); // yM + if (*ss=='z') _cimg_mp_opcode0(67); // zM + if (*ss=='c') _cimg_mp_opcode0(68); // cM + } + } + if (ss3==se) { + if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3); + if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4); + if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5); + if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6); + } + + // Look for variable declarations. + for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); } + for (char *s = ss1, *ps = ss, *ns = ss2; s variable_name(ss,(unsigned int)(s-ss+1)); + variable_name.back() = 0; + bool is_valid_name = true; + if ((*ss>='0' && *ss<='9') || + (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' || + *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' || + *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) || + (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') || + (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v')) || + (*ss=='x' && (*(ss+1)=='m' || *(ss+1)=='M')) || + (*ss=='y' && (*(ss+1)=='m' || *(ss+1)=='M')) || + (*ss=='z' && (*(ss+1)=='m' || *(ss+1)=='M')) || + (*ss=='c' && (*(ss+1)=='m' || *(ss+1)=='M'))))) is_valid_name = false; + for (const char *ns = ss; ns'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { + is_valid_name = false; break; + } + if (!is_valid_name) { + *se = saved_char; + if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") || + !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") || + !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") || + !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") || + !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") || + !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv") || + !std::strcmp(variable_name,"xm") || !std::strcmp(variable_name,"ym") || + !std::strcmp(variable_name,"zm") || !std::strcmp(variable_name,"cm") || + !std::strcmp(variable_name,"xM") || !std::strcmp(variable_name,"yM") || + !std::strcmp(variable_name,"zM") || !std::strcmp(variable_name,"cM")) + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid assignment of reserved variable name '%s' in specified expression '%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss-8)>expr._data?"...":"", + (ss-8)>expr._data?ss-8:expr._data, + se<&expr.back()?"...":""); + else + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid variable name '%s' in specified expression '%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss-8)>expr._data?"...":"", + (ss-8)>expr._data?ss-8:expr._data, + se<&expr.back()?"...":""); + } + for (unsigned int i = 0; i::%s(): Invalid multiple assignments of variable '%s' in specified expression '%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss-8)>expr._data?"...":"", + (ss-8)>expr._data?ss-8:expr._data, + se<&expr.back()?"...":""); + } + const unsigned int src_pos = compile(s+1,se); + if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); + const unsigned int dest_pos = mempos++; + variable_name.move_to(label[dest_pos]); + CImg::vector(7,dest_pos,src_pos).move_to(code); + _cimg_mp_return(dest_pos); + } + + // Look for unary/binary operators. The operator precedences is defined as in C++. + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) { + const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+2,se); + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(8,pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1); + _cimg_mp_return(pos); + } + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) { + const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+2,se); + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(9,pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1); + _cimg_mp_return(pos); + } + for (char *s = se2; s>ss; --s) if (*s=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(10,compile(ss,s),compile(s+1,se)); + for (char *s = se2; s>ss; --s) if (*s=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(11,compile(ss,s),compile(s+1,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(12,compile(ss,s),compile(s+2,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(13,compile(ss,s),compile(s+2,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(14,compile(ss,s),compile(s+2,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(15,compile(ss,s),compile(s+2,se)); + for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) + if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se)); + for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(18,compile(ss,s),compile(s+2,se)); + for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(19,compile(ss,s),compile(s+2,se)); + for (char *s = se2, *ps = se3; s>ss; --s, --ps) + if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && + (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel) + _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se)); + for (char *s = se2, *ps = se3; s>ss; --s, --ps) + if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && + (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel) + _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se)); + for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) { + const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+1,se); + if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(22,pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1); + _cimg_mp_return(pos); + } + for (char *s = se2; s>ss; --s) if (*s=='/' && level[s-expr._data]==clevel) _cimg_mp_opcode2(23,compile(ss,s),compile(s+1,se)); + for (char *s = se2, *ns = se1; s>ss; --s, --ns) + if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel) + _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se)); + if (ssss; --s) if (*s=='^' && level[s-expr._data]==clevel) _cimg_mp_opcode2(25,compile(ss,s),compile(s+1,se)); + + // Look for a function call or a parenthesis. + if (*se1==')') { + if (*ss=='(') _cimg_mp_return(compile(ss1,se1)); + if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1)); + if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1)); + if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1)); + if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1)); + if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1)); + if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1)); + if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1)); + if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1)); + if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1)); + if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1)); + if (!std::strncmp(ss,"log2(",5)) _cimg_mp_opcode1(71,compile(ss5,se1)); + if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1)); + if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1)); + if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1)); + if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1)); + if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1)); + if (!std::strncmp(ss,"atan2(",6)) { + char *s1 = ss6; while (s1=mem._width) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(45,pos,mem_cond,mem_A,mem_B,bp2-bp1,code._width-bp2).move_to(code,bp1); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"round(",6)) { + unsigned int value = 0, round = 1, direction = 0; + char *s1 = ss6; while (s1 opcode; + if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(ss[1]=='i'?48:49,pos).move_to(opcode); + for (char *s = ss4; s::vector(compile(s,ns)).move_to(opcode); + s = ns; + } + (opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"arg(",4)) { + CImgList opcode; + if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + CImg::vector(69,pos).move_to(opcode); + for (char *s = ss4; s::vector(compile(s,ns)).move_to(opcode); + s = ns; + } + (opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"narg(",5)) { + if (*ss5==')') _cimg_mp_return(0); + unsigned int nb_args = 0; + for (char *s = ss5; s=mem.size()) mem.resize(-200,1,1,1,0); + const unsigned int pos = mempos++; + mem[pos] = nb_args; + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"isval(",6)) { + char sep = 0, end = 0; double val = 0; + if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); + _cimg_mp_return(0); + } + if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1)); + if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1)); + if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1)); + if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1)); + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { + unsigned int value = 0, nb = 1; + char *s1 = ss4; while (s1 variable_name(ss,(unsigned int)(se-ss+1)); + variable_name.back() = 0; + for (unsigned int i = 0; i::%s(): Invalid item '%s' in specified expression '%s%s%s'.\n", + pixel_type(),calling_function, + variable_name._data, + (ss-8)>expr._data?"...":"", + (ss-8)>expr._data?ss-8:expr._data, + se<&expr.back()?"...":""); + return 0; + } + + // Evaluation functions, known by the parser. + double mp_u() { + return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]); + } + double mp_g() { + return cimg::grand(); + } + double mp_i() { + return (double)reference.atXYZC((int)mem[9],(int)mem[10],(int)mem[11],(int)mem[12],0); + } + double mp_xw() { + return mem[9]/reference.width(); + } + double mp_yh() { + return mem[10]/reference.height(); + } + double mp_zd() { + return mem[11]/reference.depth(); + } + double mp_cs() { + return mem[12]/reference.spectrum(); + } + double mp_equal() { + return mem[opcode[2]]; + } + double mp_logical_and() { + const bool is_A = (bool)mem[opcode(2)]; + const CImg *const pE = ++p_code + opcode(4); + if (!is_A) { p_code = pE - 1; return 0; } + const unsigned int mem_B = opcode(3); + for ( ; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + --p_code; + return (double)(bool)mem[mem_B]; + } + double mp_logical_or() { + const bool is_A = (bool)mem[opcode(2)]; + const CImg *const pE = ++p_code + opcode(4); + if (is_A) { p_code = pE - 1; return 1; } + const unsigned int mem_B = opcode(3); + for ( ; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + --p_code; + return (double)(bool)mem[mem_B]; + } + double mp_infeq() { + return (double)(mem[opcode(2)]<=mem[opcode(3)]); + } + double mp_supeq() { + return (double)(mem[opcode(2)]>=mem[opcode(3)]); + } + double mp_noteq() { + return (double)(mem[opcode(2)]!=mem[opcode(3)]); + } + double mp_eqeq() { + return (double)(mem[opcode(2)]==mem[opcode(3)]); + } + double mp_inf() { + return (double)(mem[opcode(2)]mem[opcode(3)]); + } + double mp_add() { + return mem[opcode(2)] + mem[opcode(3)]; + } + double mp_sub() { + return mem[opcode(2)] - mem[opcode(3)]; + } + double mp_mul() { + const double A = mem[opcode(2)]; + const CImg *const pE = ++p_code + opcode(4); + if (!A) { p_code = pE - 1; return 0; } + const unsigned int mem_B = opcode(3); + for ( ; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + --p_code; + return A*(double)mem[mem_B]; + } + double mp_div() { + return mem[opcode(2)] / mem[opcode(3)]; + } + double mp_minus() { + return -mem[opcode(2)]; + } + double mp_not() { + return !mem[opcode(2)]; + } + double mp_logical_not() { + return !mem[opcode(2)]; + } + double mp_bitwise_not() { + return ~(unsigned long)mem[opcode(2)]; + } + double mp_modulo() { + return cimg::mod(mem[opcode(2)],mem[opcode(3)]); + } + double mp_bitwise_and() { + return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]); + } + double mp_bitwise_or() { + return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]); + } + double mp_pow() { + const double v = mem[opcode(2)], p = mem[opcode(3)]; + if (p==0) return 1; + if (p==0.5) return std::sqrt(v); + if (p==1) return v; + if (p==2) return v*v; + if (p==3) return v*v*v; + if (p==4) return v*v*v*v; + return std::pow(v,p); + } + double mp_sin() { + return std::sin(mem[opcode(2)]); + } + double mp_cos() { + return std::cos(mem[opcode(2)]); + } + double mp_tan() { + return std::tan(mem[opcode(2)]); + } + double mp_asin() { + return std::asin(mem[opcode(2)]); + } + double mp_acos() { + return std::acos(mem[opcode(2)]); + } + double mp_atan() { + return std::atan(mem[opcode(2)]); + } + double mp_sinh() { + return std::sinh(mem[opcode(2)]); + } + double mp_cosh() { + return std::cosh(mem[opcode(2)]); + } + double mp_tanh() { + return std::tanh(mem[opcode(2)]); + } + double mp_log10() { + return std::log10(mem[opcode(2)]); + } + double mp_log2() { + return cimg::log2(mem[opcode(2)]); + } + double mp_log() { + return std::log(mem[opcode(2)]); + } + double mp_exp() { + return std::exp(mem[opcode(2)]); + } + double mp_sqrt() { + return std::sqrt(mem[opcode(2)]); + } + double mp_sign() { + return cimg::sign(mem[opcode(2)]); + } + double mp_abs() { + return cimg::abs(mem[opcode(2)]); + } + double mp_atan2() { + return std::atan2(mem[opcode(2)],mem[opcode(3)]); + } + double mp_if() { + const bool is_cond = (bool)mem[opcode(2)]; + const unsigned int mem_A = opcode(3), mem_B = opcode(4); + const CImg + *const pB = ++p_code + opcode(5), + *const pE = pB + opcode(6); + if (is_cond) { // Evaluate on-the-fly only the correct argument. + for ( ; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + p_code = pE - 1; + return mem[mem_A]; + } + for (p_code = pB; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + --p_code; + return mem[mem_B]; + } + double mp_round() { + return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]); + } + double mp_ixyzc() { + const int i = (int)mem[opcode(6)], b = (int)mem[opcode(7)]; + if (i==0) { // Nearest neighbor interpolation. + if (b==2) return (double)reference.atXYZC(cimg::mod((int)mem[opcode(2)],reference.width()), + cimg::mod((int)mem[opcode(3)],reference.height()), + cimg::mod((int)mem[opcode(4)],reference.depth()), + cimg::mod((int)mem[opcode(5)],reference.spectrum())); + if (b==1) return (double)reference.atXYZC((int)mem[opcode(2)], + (int)mem[opcode(3)], + (int)mem[opcode(4)], + (int)mem[opcode(5)]); + return (double)reference.atXYZC((int)mem[opcode(2)], + (int)mem[opcode(3)], + (int)mem[opcode(4)], + (int)mem[opcode(5)],0); + } else { // Linear interpolation. + if (b==2) return (double)reference.linear_atXYZC(cimg::mod((float)mem[opcode(2)],(float)reference.width()), + cimg::mod((float)mem[opcode(3)],(float)reference.height()), + cimg::mod((float)mem[opcode(4)],(float)reference.depth()), + cimg::mod((float)mem[opcode(5)],(float)reference.spectrum())); + if (b==1) return (double)reference.linear_atXYZC((float)mem[opcode(2)], + (float)mem[opcode(3)], + (float)mem[opcode(4)], + (float)mem[opcode(5)]); + return (double)reference.linear_atXYZC((float)mem[opcode(2)], + (float)mem[opcode(3)], + (float)mem[opcode(4)], + (float)mem[opcode(5)],0); + } + } + double mp_jxyzc() { + const double x = mem[9], y = mem[10], z = mem[11], c = mem[12]; + const int i = (int)mem[opcode(6)], b = (int)mem[opcode(7)]; + if (i==0) { // Nearest neighbor interpolation. + if (b==2) return (double)reference.atXYZC(cimg::mod((int)(x+mem[opcode(2)]),reference.width()), + cimg::mod((int)(y+mem[opcode(3)]),reference.height()), + cimg::mod((int)(z+mem[opcode(4)]),reference.depth()), + cimg::mod((int)(c+mem[opcode(5)]),reference.spectrum())); + if (b==1) return (double)reference.atXYZC((int)(x+mem[opcode(2)]), + (int)(y+mem[opcode(3)]), + (int)(z+mem[opcode(4)]), + (int)(c+mem[opcode(5)])); + return (double)reference.atXYZC((int)(x+mem[opcode(2)]), + (int)(y+mem[opcode(3)]), + (int)(z+mem[opcode(4)]), + (int)(c+mem[opcode(5)]),0); + } else { // Linear interpolation. + if (b==2) return (double)reference.linear_atXYZC(cimg::mod((float)(x+mem[opcode(2)]),(float)reference.width()), + cimg::mod((float)(y+mem[opcode(3)]),(float)reference.height()), + cimg::mod((float)(z+mem[opcode(4)]),(float)reference.depth()), + cimg::mod((float)(c+mem[opcode(5)]),(float)reference.spectrum())); + if (b==1) return (double)reference.linear_atXYZC((float)(x+mem[opcode(2)]), + (float)(y+mem[opcode(3)]), + (float)(z+mem[opcode(4)]), + (float)(c+mem[opcode(5)])); + return (double)reference.linear_atXYZC((float)(x+mem[opcode(2)]), + (float)(y+mem[opcode(3)]), + (float)(z+mem[opcode(4)]), + (float)(c+mem[opcode(5)]),0); + } + } + double mp_min() { + double val = mem[opcode(2)]; + for (unsigned int i = 3; i>(unsigned int)mem[opcode(3)]; + } + double mp_sinc() { + return cimg::sinc(mem[opcode(2)]); + } + double mp_im() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[0]:0; + } + double mp_iM() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[1]:0; + } + double mp_ia() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[2]:0; + } + double mp_iv() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[3]:0; + } + double mp_xm() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[4]:0; + } + double mp_ym() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[5]:0; + } + double mp_zm() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[6]:0; + } + double mp_cm() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[7]:0; + } + double mp_xM() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[8]:0; + } + double mp_yM() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[9]:0; + } + double mp_zM() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[10]:0; + } + double mp_cM() { + if (!reference_stats) reference.get_stats().move_to(reference_stats); + return reference_stats?reference_stats[11]:0; + } + double mp_arg() { + const int _ind = (int)mem[opcode(2)]; + const unsigned int nb_args = opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind; + if (ind>=nb_args) return 0; + return mem[opcode(ind+2)]; + } + double mp_int() { + return (double)(long)mem[opcode(2)]; + } + + // Evaluation procedure, with image data. + double eval(const double x, const double y, const double z, const double c) { + static const mp_func mp_funcs[] = { + &_cimg_math_parser::mp_u, // 0 + &_cimg_math_parser::mp_g, // 1 + &_cimg_math_parser::mp_i, // 2 + &_cimg_math_parser::mp_xw, // 3 + &_cimg_math_parser::mp_yh, // 4 + &_cimg_math_parser::mp_zd, // 5 + &_cimg_math_parser::mp_cs, // 6 + &_cimg_math_parser::mp_equal, // 7 + &_cimg_math_parser::mp_logical_or, // 8 + &_cimg_math_parser::mp_logical_and, // 9 + &_cimg_math_parser::mp_bitwise_or, // 10 + &_cimg_math_parser::mp_bitwise_and, // 11 + &_cimg_math_parser::mp_noteq, // 12 + &_cimg_math_parser::mp_eqeq, // 13 + &_cimg_math_parser::mp_infeq, // 14 + &_cimg_math_parser::mp_supeq, // 15 + &_cimg_math_parser::mp_inf, // 16 + &_cimg_math_parser::mp_sup, // 17 + &_cimg_math_parser::mp_lsl, // 18 + &_cimg_math_parser::mp_lsr, // 19 + &_cimg_math_parser::mp_sub, // 20 + &_cimg_math_parser::mp_add, // 21 + &_cimg_math_parser::mp_mul, // 22 + &_cimg_math_parser::mp_div, // 23 + &_cimg_math_parser::mp_modulo, // 24 + &_cimg_math_parser::mp_pow, // 25 + &_cimg_math_parser::mp_minus, // 26 + &_cimg_math_parser::mp_logical_not, // 27 + &_cimg_math_parser::mp_bitwise_not, // 28 + &_cimg_math_parser::mp_sin, // 29 + &_cimg_math_parser::mp_cos, // 30 + &_cimg_math_parser::mp_tan, // 31 + &_cimg_math_parser::mp_asin, // 32 + &_cimg_math_parser::mp_acos, // 33 + &_cimg_math_parser::mp_atan, // 34 + &_cimg_math_parser::mp_sinh, // 35 + &_cimg_math_parser::mp_cosh, // 36 + &_cimg_math_parser::mp_tanh, // 37 + &_cimg_math_parser::mp_log10, // 38 + &_cimg_math_parser::mp_log, // 39 + &_cimg_math_parser::mp_exp, // 40 + &_cimg_math_parser::mp_sqrt, // 41 + &_cimg_math_parser::mp_sign, // 42 + &_cimg_math_parser::mp_abs, // 43 + &_cimg_math_parser::mp_atan2, // 44 + &_cimg_math_parser::mp_if, // 45 + &_cimg_math_parser::mp_round, // 46 + &_cimg_math_parser::mp_ixyzc, // 47 + &_cimg_math_parser::mp_min, // 48 + &_cimg_math_parser::mp_max, // 49 + &_cimg_math_parser::mp_isnan, // 50 + &_cimg_math_parser::mp_isinf, // 51 + &_cimg_math_parser::mp_isint, // 52 + &_cimg_math_parser::mp_isbool, // 53 + &_cimg_math_parser::mp_rol, // 54 + &_cimg_math_parser::mp_ror, // 55 + &_cimg_math_parser::mp_sinc, // 56 + &_cimg_math_parser::mp_im, // 57 + &_cimg_math_parser::mp_iM, // 58 + &_cimg_math_parser::mp_ia, // 59 + &_cimg_math_parser::mp_iv, // 60 + &_cimg_math_parser::mp_xm, // 61 + &_cimg_math_parser::mp_ym, // 62 + &_cimg_math_parser::mp_zm, // 63 + &_cimg_math_parser::mp_cm, // 64 + &_cimg_math_parser::mp_xM, // 65 + &_cimg_math_parser::mp_yM, // 66 + &_cimg_math_parser::mp_zM, // 67 + &_cimg_math_parser::mp_cM, // 68 + &_cimg_math_parser::mp_arg, // 69 + &_cimg_math_parser::mp_int, // 70 + &_cimg_math_parser::mp_log2, // 71 + &_cimg_math_parser::mp_jxyzc // 72 + }; + if (!mem) return 0; + this->mp_funcs = mp_funcs; + mem[9] = x; mem[10] = y; mem[11] = z; mem[12] = c; + opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1; + for (p_code = code._data; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; // Allows to avoid parameter passing to evaluation functions. + mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + } + return mem[result]; + } + }; + + //! Compute the square value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqr().normalize(0,255)).display(); + \endcode + \image html ref_sqr.jpg + **/ + CImg& sqr() { + cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); }; + return *this; + } + + //! Compute the square value of each pixel value \newinstance. + CImg get_sqr() const { + return CImg(*this,false).sqr(); + } + + //! Compute the square root of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqrt().normalize(0,255)).display(); + \endcode + \image html ref_sqrt.jpg + **/ + CImg& sqrt() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd); + return *this; + } + + //! Compute the square root of each pixel value \newinstance. + CImg get_sqrt() const { + return CImg(*this,false).sqrt(); + } + + //! Compute the exponential of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& exp() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd); + return *this; + } + + //! Compute the exponential of each pixel value \newinstance. + CImg get_exp() const { + return CImg(*this,false).exp(); + } + + //! Compute the logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& log() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd); + return *this; + } + + //! Compute the logarithm of each pixel value \newinstance. + CImg get_log() const { + return CImg(*this,false).log(); + } + + //! Compute the base-2 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& log2() { + cimg_for(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd); + return *this; + } + + //! Compute the base-10 logarithm of each pixel value \newinstance. + CImg get_log2() const { + return CImg(*this,false).log2(); + } + + //! Compute the base-10 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& log10() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd); + return *this; + } + + //! Compute the base-10 logarithm of each pixel value \newinstance. + CImg get_log10() const { + return CImg(*this,false).log10(); + } + + //! Compute the absolute value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& abs() { + cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd); + return *this; + } + + //! Compute the absolute value of each pixel value \newinstance. + CImg get_abs() const { + return CImg(*this,false).abs(); + } + + //! Compute the sign of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign \f$\mathrm{sign}(I_{(x,y,z,c)})\f$. + \note + - The sign is set to: + - \c 1 if pixel value is strictly positive. + - \c -1 if pixel value is strictly negative. + - \c 0 if pixel value is equal to \c 0. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& sign() { + cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd); + return *this; + } + + //! Compute the sign of each pixel value \newinstance. + CImg get_sign() const { + return CImg(*this,false).sign(); + } + + //! Compute the cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& cos() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd); + return *this; + } + + //! Compute the cosine of each pixel value \newinstance. + CImg get_cos() const { + return CImg(*this,false).cos(); + } + + //! Compute the sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& sin() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd); + return *this; + } + + //! Compute the sine of each pixel value \newinstance. + CImg get_sin() const { + return CImg(*this,false).sin(); + } + + //! Compute the sinc of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& sinc() { + cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd); + return *this; + } + + //! Compute the sinc of each pixel value \newinstance. + CImg get_sinc() const { + return CImg(*this,false).sinc(); + } + + //! Compute the tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& tan() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd); + return *this; + } + + //! Compute the tangent of each pixel value \newinstance. + CImg get_tan() const { + return CImg(*this,false).tan(); + } + + //! Compute the hyperbolic cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& cosh() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd); + return *this; + } + + //! Compute the hyperbolic cosine of each pixel value \newinstance. + CImg get_cosh() const { + return CImg(*this,false).cosh(); + } + + //! Compute the hyperbolic sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& sinh() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd); + return *this; + } + + //! Compute the hyperbolic sine of each pixel value \newinstance. + CImg get_sinh() const { + return CImg(*this,false).sinh(); + } + + //! Compute the hyperbolic tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& tanh() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd); + return *this; + } + + //! Compute the hyperbolic tangent of each pixel value \newinstance. + CImg get_tanh() const { + return CImg(*this,false).tanh(); + } + + //! Compute the arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine \f$\mathrm{acos}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& acos() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd); + return *this; + } + + //! Compute the arccosine of each pixel value \newinstance. + CImg get_acos() const { + return CImg(*this,false).acos(); + } + + //! Compute the arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine \f$\mathrm{asin}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& asin() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd); + return *this; + } + + //! Compute the arcsine of each pixel value \newinstance. + CImg get_asin() const { + return CImg(*this,false).asin(); + } + + //! Compute the arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent \f$\mathrm{atan}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + CImg& atan() { + cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd); + return *this; + } + + //! Compute the arctangent of each pixel value \newinstance. + CImg get_atan() const { + return CImg(*this,false).atan(); + } + + //! Compute the arctangent2 of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$. + \param img Image whose pixel values specify the second argument of the \c atan2() function. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2'. + img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2'. + img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value. + (img_x,img_y,img_atan2).display(); + \endcode + **/ + template + CImg& atan2(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return atan2(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_atan2(const CImg& img) const { + return CImg(*this,false).atan2(img); + } + + //! In-place pointwise multiplication. + /** + Compute the pointwise multiplication between the image instance and the specified input image \c img. + \param img Input image, as the second operand of the multiplication. + \note + - Similar to operator+=(const CImg&), except that it performs a pointwise multiplication instead of an addition. + - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg&) instead. + \par Example + \code + CImg + img("reference.jpg"), + shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false); + shade.normalize(0,1); + (img,shade,img.get_mul(shade)).display(); + \endcode + **/ + template + CImg& mul(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return mul(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_mul(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).mul(img); + } + + //! In-place pointwise division. + /** + Similar to mul(const CImg&), except that it performs a pointwise division instead of a multiplication. + **/ + template + CImg& div(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return div(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_div(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).div(img); + } + + //! Raise each pixel value to a specified power. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$. + \param p Exponent value. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img0("reference.jpg"), // Load reference color image. + img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8. + img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5. + (img0,img1,img2).display(); + \endcode + **/ + CImg& pow(const double p) { + if (p==-4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); } return *this; } + if (p==-3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); } return *this; } + if (p==-2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); } return *this; } + if (p==-1) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); } return *this; } + if (p==-0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); } return *this; } + if (p==0) return fill(1); + if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; } + if (p==1) return *this; + if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; } + if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; } + if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; } + cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p); + return *this; + } + + //! Raise each pixel value to a specified power \newinstance. + CImg get_pow(const double p) const { + return CImg(*this,false).pow(p); + } + + //! Raise each pixel value to a power, specified from an expression. + /** + Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. + **/ + CImg& pow(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"pow"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + CImg values(_width,_height,_depth,_spectrum); + try { + values.fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + values.load(expression); + } + pow(values); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Raise each pixel value to a power, specified from an expression \newinstance. + CImg get_pow(const char *const expression) const { + return CImg(*this,false).pow(expression); + } + + //! Raise each pixel value to a power, pointwisely specified from another image. + /** + Similar to operator+=(const CImg& img), except that it performs an exponentiation instead of an addition. + **/ + template + CImg& pow(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return pow(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_pow(const CImg& img) const { + return CImg(*this,false).pow(img); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const unsigned int n=1) { + cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n); + return *this; + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const unsigned int n=1) const { + return (+*this).rol(n); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"rol"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + CImg values(_width,_height,_depth,_spectrum); + try { + values.fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + values.load(expression); + } + rol(values); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const char *const expression) const { + return (+*this).rol(expression); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const CImg&), except that it performs a left rotation instead of a left shift. + **/ + template + CImg& rol(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return rol(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_rol(const CImg& img) const { + return (+*this).rol(img); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const unsigned int n=1) { + cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n); + return *this; + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const unsigned int n=1) const { + return (+*this).ror(n); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"ror"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + CImg values(_width,_height,_depth,_spectrum); + try { + values.fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + values.load(expression); + } + ror(values); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const char *const expression) const { + return (+*this).ror(expression); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const CImg&), except that it performs a right rotation instead of a right shift. + **/ + template + CImg& ror(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return ror(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_ror(const CImg& img) const { + return (+*this).ror(img); + } + + //! Pointwise min operator between instance image and a value. + /** + \param val Value used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& min(const T val) { + cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val); + return *this; + } + + //! Pointwise min operator between instance image and a value \newinstance. + CImg get_min(const T val) const { + return (+*this).min(val); + } + + //! Pointwise min operator between two images. + /** + \param img Image used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& min(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return min(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_min(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).min(img); + } + + //! Pointwise min operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& min(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"min"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + CImg values(_width,_height,_depth,_spectrum); + try { + values.fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + values.load(expression); + } + min(values); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Pointwise min operator between an image and an expression \newinstance. + CImg get_min(const char *const expression) const { + return CImg(*this,false).min(expression); + } + + //! Pointwise max operator between instance image and a value. + /** + \param val Value used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& max(const T val) { + cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val); + return *this; + } + + //! Pointwise max operator between instance image and a value \newinstance. + CImg get_max(const T val) const { + return (+*this).max(val); + } + + //! Pointwise max operator between two images. + /** + \param img Image used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& max(const CImg& img) { + const unsigned long siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return max(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (unsigned long n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_max(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).max(img); + } + + //! Pointwise max operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& max(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"max"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); --ptrd; } + else cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } + } catch (CImgException&) { + CImg values(_width,_height,_depth,_spectrum); + try { + values.fill(expression,true); + } catch (CImgException&) { + cimg::exception_mode() = omode; + values.load(expression); + } + max(values); + } + cimg::exception_mode() = omode; + return *this; + } + + //! Pointwise max operator between an image and an expression \newinstance. + CImg get_max(const char *const expression) const { + return CImg(*this,false).max(expression); + } + + //! Return a reference to the minimum pixel value. + /** + **/ + T& min() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min; + cimg_for(*this,ptrs,T) if (*ptrsmax_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value \const. + const T& max() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max; + cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value. + /** + \param[out] max_val Maximum pixel value. + **/ + template + T& min_max(t& max_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. + template + const T& min_max(t& max_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + const T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the maximum pixel value as well as the minimum pixel value. + /** + \param[out] min_val Minimum pixel value. + **/ + template + T& max_min(t& min_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val arr(*this); + unsigned int l = 0, ir = size() - 1; + for (;;) { + if (ir<=l+1) { + if (ir==l+1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l+1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]); + if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]); + unsigned int i = l + 1, j = ir; + const T pivot = arr[l+1]; + for (;;) { + do ++i; while (arr[i]pivot); + if (j=k) ir = j - 1; + if (j<=k) l = i; + } + } + return 0; + } + + //! Return the median pixel value. + /** + **/ + T median() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "median(): Empty instance.", + cimg_instance); + const unsigned int s = size(); + const T res = kth_smallest(s>>1); + return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); + } + + //! Return the sum of all the pixel values. + /** + **/ + Tdouble sum() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "sum(): Empty instance.", + cimg_instance); + Tdouble res = 0; + cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs; + return res; + } + + //! Return the average pixel value. + /** + **/ + Tdouble mean() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "mean(): Empty instance.", + cimg_instance); + Tdouble res = 0; + cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs; + return res/size(); + } + + //! Return the variance of the pixel values. + /** + \param variance_method Method used to estimate the variance. Can be: + - \c 0: Second moment, computed as + \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$ + with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$. + - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$. + - \c 2: Least median of squares. + - \c 3: Least trimmed of squares. + **/ + Tdouble variance(const unsigned int variance_method=1) const { + Tdouble foo; + return variance_mean(variance_method,foo); + } + + //! Return the variance as well as the average of the pixel values. + /** + \param variance_method Method used to estimate the variance (see variance(const unsigned int) const). + \param[out] mean Average pixel value. + **/ + template + Tdouble variance_mean(const unsigned int variance_method, t& mean) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_mean(): Empty instance.", + cimg_instance); + + Tdouble variance = 0, average = 0; + const unsigned long siz = size(); + switch (variance_method) { + case 0 :{ // Least mean square (standard definition) + Tdouble S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; } + variance = (S2 - S*S/siz)/siz; + average = S; + } break; + case 1 : { // Least mean square (robust definition) + Tdouble S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; } + variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + average = S; + } break; + case 2 : { // Least Median of Squares (MAD) + CImg buf(*this); + buf.sort(); + const unsigned long siz2 = siz>>1; + const Tdouble med_i = (double)buf[siz2]; + cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; } + buf.sort(); + const Tdouble sig = (Tdouble)(1.4828*buf[siz2]); + variance = sig*sig; + } break; + default : { // Least trimmed of Squares + CImg buf(*this); + const unsigned long siz2 = siz>>1; + cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; } + buf.sort(); + Tdouble a = 0; + const Tfloat *ptrs = buf._data; + for (unsigned long j = 0; j0?variance:0; + } + + //! Return estimated variance of the noise. + /** + \param variance_method Method used to compute the variance (see variance(const unsigned int) const). + \note Because of structures such as edges in images it is + recommanded to use a robust variance estimation. The variance of the + noise is estimated by computing the variance of the Laplacian \f$(\Delta + I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]= + \sigma^2\f$ where \f$\sigma\f$ is the noise variance. + **/ + Tdouble variance_noise(const unsigned int variance_method=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_noise(): Empty instance.", + cimg_instance); + + const unsigned long siz = size(); + if (!siz || !_data) return 0; + if (variance_method>1) { // Compute a scaled version of the Laplacian. + CImg tmp(*this); + if (_depth==1) { + const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. + CImg_3x3(I,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn + + (Tdouble)Icp - 4*(Tdouble)Icc); + } + } else { + const Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. + CImg_3x3x3(I,T); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { + tmp(x,y,z,c) = cste*( + (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc + + (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc); + } + } + return tmp.variance(variance_method); + } + + // Version that doesn't need intermediate images. + Tdouble variance = 0, S = 0, S2 = 0; + if (_depth==1) { + const Tdouble cste = 1.0/std::sqrt(20.0); + CImg_3x3(I,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc + + (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc); + S+=val; S2+=val*val; + } + } else { + const Tdouble cste = 1.0/std::sqrt(42.0); + CImg_3x3x3(I,T); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { + const Tdouble val = cste * + ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + + (Tdouble)Icpc + + (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc); + S+=val; S2+=val*val; + } + } + if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + else variance = (S2 - S*S/siz)/siz; + return variance>0?variance:0; + } + + //! Compute the MSE (Mean-Squared Error) between two images. + /** + \param img Image used as the second argument of the MSE operator. + **/ + template + Tdouble MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException(_cimg_instance + "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + Tdouble vMSE = 0; + const t* ptr2 = img._data; + cimg_for(*this,ptr1,T) { + const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(ptr2++); + vMSE+=diff*diff; + } + const unsigned long siz = img.size(); + if (siz) vMSE/=siz; + return vMSE; + } + + //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. + /** + \param img Image used as the second argument of the PSNR operator. + \param max_value Maximum theoretical value of the signal. + **/ + template + Tdouble PSNR(const CImg& img, const Tdouble max_value=255) const { + const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img)); + return (vMSE!=0)?(Tdouble)(20*std::log10(max_value/vMSE)):(Tdouble)(cimg::type::max()); + } + + //! Evaluate math formula. + /** + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. + **/ + double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const { + static _cimg_math_parser *mp = 0; + if (expression) { delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); } + return mp?mp->eval(x,y,z,c):0; + } + + //! Compute statistics vector from the pixel values. + /** + \param variance_method Method used to compute the variance (see variance(const unsigned int) const). + \return Statistics vector as [min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax]. + **/ + CImg get_stats(const unsigned int variance_method=1) const { + if (is_empty()) return CImg(); + const unsigned long siz = size(); + const T *const odata = _data; + const T *pm = odata, *pM = odata; + Tdouble S = 0, S2 = 0; + T m = *pm, M = m; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + const Tdouble _val = (Tdouble)val; + if (valM) { M = val; pM = ptrs; } + S+=_val; + S2+=_val*_val; + } + const Tdouble + 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, cm = 0, + xM = 0, yM = 0, zM = 0, cM = 0; + contains(*pm,xm,ym,zm,cm); + contains(*pM,xM,yM,zM,cM); + return CImg(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value, + (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm, + (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM); + } + + //! Compute statistics vector from the pixel values \inplace. + CImg& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).move_to(*this); + } + + //@} + //------------------------------------- + // + //! \name Vector / Matrix Operations + //@{ + //------------------------------------- + + //! Compute norm of the image, viewed as a matrix. + /** + \param magnitude_type Norm type. Can be: + - \c -1: Linf-norm + - \c 0: L2-norm + - \c 1: L1-norm + **/ + Tdouble magnitude(const int magnitude_type=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "magnitude(): Empty instance.", + cimg_instance); + Tdouble res = 0; + switch (magnitude_type) { + case -1 : { + cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; } + } break; + case 1 : { + cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs); + } break; + default : { + cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs); + res = (Tdouble)std::sqrt(res); + } + } + return res; + } + + //! Compute the trace of the image, viewed as a matrix. + /** + **/ + Tdouble trace() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "trace(): Empty instance.", + cimg_instance); + Tdouble res = 0; + cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k); + return res; + } + + //! Compute the determinant of the image, viewed as a matrix. + /** + **/ + Tdouble det() const { + if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "det(): Instance is not a square matrix.", + cimg_instance); + + switch (_width) { + case 1 : return (Tdouble)((*this)(0,0)); + case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0)); + case 3 : { + const Tdouble + a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2], + b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5], + c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8]; + return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; + } + default : { + CImg lu(*this); + CImg indx; + bool d; + lu._LU(indx,d); + Tdouble res = d?(Tdouble)1:(Tdouble)-1; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + return 0; + } + + //! Compute the dot product between instance and argument, viewed as matrices. + /** + \param img Image used as a second argument of the dot product. + **/ + template + Tdouble dot(const CImg& img) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "dot(): Empty instance.", + cimg_instance); + if (!img) + throw CImgArgumentException(_cimg_instance + "dot(): Empty specified image.", + cimg_instance); + + const unsigned int nb = cimg::min(size(),img.size()); + Tdouble res = 0; + for (unsigned int off = 0; off get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + _cimg_static CImg res; + if (res._height!=_spectrum) res.assign(1,_spectrum); + const unsigned long whd = (unsigned long)_width*_height*_depth; + const T *ptrs = data(x,y,z); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get (square) matrix-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \note - The spectrum() of the image must be a square. + **/ + CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)std::sqrt((double)_spectrum); + const T *ptrs = data(x,y,z,0); + const unsigned long whd = (unsigned long)_width*_height*_depth; + CImg res(n,n); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get tensor-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + const T *ptrs = data(x,y,z,0); + const unsigned long whd = (unsigned long)_width*_height*_depth; + if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd)); + if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd)); + return tensor(*ptrs); + } + + //! Set vector-valued pixel at specified position. + /** + \param vec Vector to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { + if (x<_width && y<_height && z<_depth) { + const t *ptrs = vec._data; + const unsigned long whd = (unsigned long)_width*_height*_depth; + T *ptrd = data(x,y,z); + for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; } + } + return *this; + } + + //! Set (square) matrix-valued pixel at specified position. + /** + \param mat Matrix to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Set tensor-valued pixel at specified position. + /** + \param ten Tensor to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + T *ptrd = data(x,y,z,0); + const unsigned long siz = (unsigned long)_width*_height*_depth; + if (ten._height==2) { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[3]; + } + else { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[2]; ptrd+=siz; + *ptrd = (T)ten[4]; ptrd+=siz; + *ptrd = (T)ten[5]; ptrd+=siz; + *ptrd = (T)ten[8]; + } + return *this; + } + + //! Unroll pixel values along axis \c y. + /** + \note Equivalent to \code unroll('y'); \endcode. + **/ + CImg& vector() { + return unroll('y'); + } + + //! Unroll pixel values along axis \c y \newinstance. + CImg get_vector() const { + return get_unroll('y'); + } + + //! Resize image to become a scalar square matrix. + /** + **/ + CImg& matrix() { + const unsigned long 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 long i = 11, i2 = i*i; + while (i2 get_matrix() const { + return (+*this).matrix(); + } + + //! Resize image to become a symmetric tensor. + /** + **/ + CImg& tensor() { + return get_tensor().move_to(*this); + } + + //! Resize image to become a symmetric tensor \newinstance. + CImg get_tensor() const { + CImg res; + const unsigned long 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_instance + "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).", + cimg_instance); + } + return res; + } + + //! Resize image to become a diagonal matrix. + /** + \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient. + **/ + CImg& diagonal() { + return get_diagonal().move_to(*this); + } + + //! Resize image to become a diagonal matrix \newinstance. + CImg get_diagonal() const { + if (is_empty()) return *this; + CImg res(size(),size(),1,1,0); + cimg_foroff(*this,off) res(off,off) = (*this)(off); + return res; + } + + //! Replace the image by an identity matrix. + /** + \note If the instance image is not square, it is resized to a square matrix using its maximum dimension as a reference. + **/ + CImg& identity_matrix() { + return identity_matrix(cimg::max(_width,_height)).move_to(*this); + } + + //! Replace the image by an identity matrix \newinstance. + CImg get_identity_matrix() const { + return identity_matrix(cimg::max(_width,_height)); + } + + //! Fill image with a linear sequence of values. + /** + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + CImg& sequence(const T a0, const T a1) { + if (is_empty()) return *this; + const unsigned int siz = size() - 1; + T* ptr = _data; + if (siz) { + const Tdouble delta = (Tdouble)a1 - (Tdouble)a0; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } else *ptr = a0; + return *this; + } + + //! Fill image with a linear sequence of values \newinstance. + CImg get_sequence(const T a0, const T a1) const { + return (+*this).sequence(a0,a1); + } + + //! Transpose the image, viewed as a matrix. + /** + \note Equivalent to \code permute_axes("yxzc"); \endcode + **/ + CImg& transpose() { + if (_width==1) { _width = _height; _height = 1; return *this; } + if (_height==1) { _height = _width; _width = 1; return *this; } + if (_width==_height) { + cimg_forYZC(*this,y,z,c) for (int x = y; x get_transpose() const { + return get_permute_axes("yxzc"); + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors. + /** + \param img Image used as the second argument of the cross product. + \note The first argument of the cross product is \c *this. + **/ + template + CImg& cross(const CImg& img) { + if (_width!=1 || _height<3 || img._width!=1 || img._height<3) + throw CImgInstanceException(_cimg_instance + "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,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; + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance. + template + CImg<_cimg_Tt> get_cross(const CImg& img) const { + return CImg<_cimg_Tt>(*this).cross(img); + } + + //! Invert the instance image, viewed as a matrix. + /** + \param use_LU Choose the inverting algorithm. Can be: + - \c true: LU-based matrix inversion. + - \c false: SVD-based matrix inversion. + **/ + CImg& invert(const bool use_LU=true) { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "invert(): Instance is not a square matrix.", + cimg_instance); +#ifdef cimg_use_lapack + int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N]; + Tfloat + *const lapA = new Tfloat[N*N], + *const 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_instance + "invert(): LAPACK function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetri_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] WORK; +#else + const double dete = _width>3?-1.0:det(); + if (dete!=0.0 && _width==2) { + const double + a = _data[0], c = _data[1], + b = _data[2], d = _data[3]; + _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete); + _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete); + } else if (dete!=0.0 && _width==3) { + const double + a = _data[0], d = _data[1], g = _data[2], + b = _data[3], e = _data[4], h = _data[5], + c = _data[6], f = _data[7], i = _data[8]; + _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete); + _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete); + _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete); + } else { + if (use_LU) { // LU-based inverse computation + CImg A(*this), indx, col(1,_width); + bool d; + A._LU(indx,d); + cimg_forX(*this,j) { + col.fill(0); + col(j) = 1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = (T)col(i); + } + } else { // SVD-based inverse computation + CImg U(_width,_width), S(1,_width), V(_width,_width); + SVD(U,S,V,false); + U.transpose(); + cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; + S.diagonal(); + *this = V*S*U; + } + } +#endif + return *this; + } + + //! Invert the instance image, viewed as a matrix \newinstance. + CImg get_invert(const bool use_LU=true) const { + return CImg(*this,false).invert(use_LU); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. + /** + **/ + CImg& pseudoinvert() { + return get_pseudoinvert().move_to(*this); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. + CImg get_pseudoinvert() const { + CImg U, S, V; + SVD(U,S,V); + const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*cimg::max(_width,_height)*S.max(); + cimg_forX(V,x) { + const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0; + cimg_forY(V,y) V(x,y)*=invs; + } + return V*U.transpose(); + } + + //! Solve a system of linear equations. + /** + \param A Matrix of the linear system. + \note Solve \c AX=B where \c B=*this. + **/ + template + CImg& solve(const CImg& A) { + if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + if (A._width==A._height) { +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N]; + Ttfloat + *const lapA = new Ttfloat[N*N], + *const lapB = new Ttfloat[N], + *const 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_instance + "solve(): LAPACK library function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + + if (!INFO) { + cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrs_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; +#else + CImg lu(A,false); + CImg indx; + bool d; + lu._LU(indx,d); + _solve(lu,indx); +#endif + } else { // Least-square solution for non-square systems. +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width; + Ttfloat WORK_QUERY; + Ttfloat + * const lapA = new Ttfloat[M*N], + * const lapB = new Ttfloat[M*NRHS]; + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO); + LWORK = (int) WORK_QUERY; + Ttfloat *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*M+l] = (Ttfloat)(A(k,l)); + cimg_forXY(*this,k,l) lapB[k*M+l] = (Ttfloat)((*this)(k,l)); + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO); + if (INFO != 0) + cimg::warn(_cimg_instance + "solve(): LAPACK library function sgels() returned error code %d.", + cimg_instance, + INFO); + assign(NRHS, N); + if (!INFO != 0) + cimg_forXY(*this,k,l) (*this)(k,l) = (T) lapB[k*M+l]; + else + assign(A.get_pseudoinvert()*(*this)); + delete[] lapA; delete[] lapB; delete[] WORK; +#else + assign(A.get_pseudoinvert()*(*this)); +#endif + } + return *this; + } + + //! Solve a system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve(const CImg& A) const { + return CImg<_cimg_Ttfloat>(*this,false).solve(A); + } + + template + CImg& _solve(const CImg& A, const CImg& indx) { + typedef _cimg_Ttfloat Ttfloat; + const int N = size(); + int ii = -1; + Ttfloat sum; + for (int i = 0; i=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii = i; + (*this)(i) = (T)sum; + } + for (int i = N - 1; i>=0; --i) { + sum = (*this)(i); + for (int j = i + 1; j + CImg& solve_tridiagonal(const CImg& A) { + const unsigned int siz = (int)size(); + if (A._width!=3 || A._height!=siz) + throw CImgArgumentException(_cimg_instance + "solve_tridiagonal(): Instance and tridiagonal matrix " + "(%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = 1e-4; + CImg B = A.get_column(1), V(*this,false); + for (int i = 1; i<(int)siz; ++i) { + const Ttfloat m = A(0,i)/(B[i-1]?B[i-1]:epsilon); + B[i] -= m*A(2,i-1); + V[i] -= m*V[i-1]; + } + (*this)[siz-1] = (T)(V[siz-1]/(B[siz-1]?B[siz-1]:epsilon)); + for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i+1])/(B[i]?B[i]:epsilon)); + return *this; + } + + //! Solve a tridiagonal system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg& A) const { + return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvalues, sorted by columns. + **/ + template + const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + + if (val.size()<(unsigned long)_width) val.assign(1,_width); + if (vec.size()<(unsigned long)_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_instance + "eigen(): Complex eigenvalues found.", + cimg_instance); + + f = std::sqrt(f); + const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); + const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b); + val[0] = (t)l2; + val[1] = (t)l1; + vec(0,0) = (t)std::cos(theta1); + vec(0,1) = (t)std::sin(theta1); + vec(1,0) = (t)std::cos(theta2); + vec(1,1) = (t)std::sin(theta2); + } break; + default : + throw CImgInstanceException(_cimg_instance + "eigen(): Eigenvalues computation of general matrices is limited to 2x2 matrices.", + cimg_instance); + } + } + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \return A list of two images [val; vec], whose meaning is similar as in eigen(CImg&,CImg&) const. + **/ + CImgList get_eigen() const { + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvalues, sorted by columns. + **/ + template + const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { +#ifdef cimg_use_lapack + char JOB = 'V', UPLO = 'U'; + int N = _width, LWORK = 4*N, INFO; + Tfloat + *const lapA = new Tfloat[N*N], + *const lapW = new Tfloat[N], + *const 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_instance + "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.", + cimg_instance, + 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 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + + val.assign(1,_width); + if (vec._data) vec.assign(_width,_width); + if (_width<3) { + eigen(val,vec); + if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices. + return *this; + } + CImg V(_width,_width); + SVD(vec,val,V,false); + bool is_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) is_ambiguous = true; + if (scal<0) val[p] = -val[p]; + } + if (is_ambiguous) { + ++(eig*=2); + SVD(vec,val,V,false,40,eig); + val-=eig; + } + CImg permutations; // sort eigenvalues in decreasing order + CImg tmp(_width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k); + std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width); + } +#endif + } + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \return A list of two images [val; vec], whose meaning are similar as in symmetric_eigen(CImg&,CImg&) const. + **/ + CImgList get_symmetric_eigen() const { + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //! Sort pixel values and get sorting permutations. + /** + \param[out] permutations Permutation map used for the sorting. + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + **/ + template + CImg& sort(CImg& permutations, const bool is_increasing=true) { + permutations.assign(_width,_height,_depth,_spectrum); + if (is_empty()) return *this; + cimg_foroff(permutations,off) permutations[off] = (t)off; + return _quicksort(0,size()-1,permutations,is_increasing,true); + } + + //! Sort pixel values and get sorting permutations \newinstance. + template + CImg get_sort(CImg& permutations, const bool is_increasing=true) const { + return (+*this).sort(permutations,is_increasing); + } + + //! Sort pixel values. + /** + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + \param axis Tells if the value sorting must be done along a specific axis. Can be: + - \c 0: All pixel values are sorted, independently on their initial position. + - \c 'x': Image columns are sorted, according to the first value in each column. + - \c 'y': Image rows are sorted, according to the first value in each row. + - \c 'z': Image slices are sorted, according to the first value in each slice. + - \c 'c': Image channels are sorted, according to the first value in each channel. + **/ + CImg& sort(const bool is_increasing=true, const char axis=0) { + if (is_empty()) return *this; + CImg perm; + switch (cimg::uncase(axis)) { + case 0 : + _quicksort(0,size()-1,perm,is_increasing,false); + break; + case 'x' : { + perm.assign(_width); + get_crop(0,0,0,0,_width-1,0,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c); + } break; + case 'y' : { + perm.assign(_height); + get_crop(0,0,0,0,0,_height-1,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c); + } break; + case 'z' : { + perm.assign(_depth); + get_crop(0,0,0,0,0,0,_depth-1,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c); + } break; + case 'c' : { + perm.assign(_spectrum); + get_crop(0,0,0,0,0,0,0,_spectrum-1).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]); + } break; + default : + throw CImgArgumentException(_cimg_instance + "sort(): Invalid specified axis '%c' " + "(should be { x | y | z | c }).", + cimg_instance,axis); + } + return *this; + } + + //! Sort pixel values \newinstance. + CImg get_sort(const bool is_increasing=true, const char axis=0) const { + return (+*this).sort(is_increasing,axis); + } + + template + CImg& _quicksort(const int indm, const int indM, CImg& permutations, const bool is_increasing, const bool is_permutations) { + if (indm(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]>(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]>(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } else { + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]<(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } + if (indM - indm>=3) { + const T pivot = (*this)[mid]; + int i = indm, j = indM; + if (is_increasing) { + do { + while ((*this)[i]pivot) --j; + if (i<=j) { + if (is_permutations) cimg::swap(permutations[i],permutations[j]); + cimg::swap((*this)[i++],(*this)[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) ++i; + while ((*this)[j] A; // Input matrix (assumed to contain some values). + CImg<> U,S,V; + A.SVD(U,S,V) + \endcode + **/ + template + const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else { + U = *this; + if (lambda!=0) { + const unsigned int delta = cimg::min(U._width,U._height); + for (unsigned int i = 0; i rv1(_width); + t anorm = 0, c, f, g = 0, h, s, scale = 0; + int l = 0, nm = 0; + + cimg_forX(U,i) { + l = i+1; rv1[i] = scale*g; g = s = scale = 0; + if (i=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g; + for (int j = l; j=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g; + for (int k = l; k=0; --i) { + if (i=0; --i) { + l = i+1; g = S[i]; + for (int j = l; j=0; --k) { + for (unsigned int its = 0; its=1; --l) { + nm = l-1; + if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm])+anorm)==anorm) break; + } + if (flag) { + c = 0; s = 1; + for (int i = l; i<=k; ++i) { + f = s*rv1[i]; rv1[i] = c*rv1[i]; + if ((cimg::abs(f)+anorm)==anorm) break; + g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; + cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c + z*s; U(i,j) = z*c - y*s; } + } + } + const t z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k-1; + t x = S[l], y = S[nm]; + g = rv1[nm]; h = rv1[k]; + f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y); + g = (t)cimg::_pythagore(f,1.0); + f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; + c = s = 1; + for (int j = l; j<=nm; ++j) { + const int i = j+1; + g = rv1[i]; h = s*g; g = c*g; + t y = S[i]; + t z = (t)cimg::_pythagore(f,h); + rv1[j] = z; c = f/z; s = h/z; + f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; + cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c + z*s; V(i,jj) = z*c - x*s; } + z = (t)cimg::_pythagore(f,h); S[j] = z; + if (z) { z = 1/z; c = f*z; s = h*z; } + f = c*g+s*y; x = c*y-s*g; + cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c + z*s; U(i,jj) = z*c - y*s; } + } + rv1[l] = 0; rv1[k]=f; S[k]=x; + } + } + + if (sorting) { + CImg permutations; + CImg tmp(_width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forY(permutations,y) tmp(y) = U(permutations(y),k); + std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width); + } + cimg_forY(V,k) { + cimg_forY(permutations,y) tmp(y) = V(permutations(y),k); + std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width); + } + } + } + return *this; + } + + //! Compute the SVD of the instance image, viewed as a general matrix. + /** + \return A list of three images [U; S; V], whose meaning is similar as in SVD(CImg&,CImg&,CImg&,bool,unsigned int,float) const. + **/ + CImgList get_SVD(const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + CImgList res(3); + SVD(res[0],res[1],res[2],sorting,max_iteration,lambda); + return res; + } + + // [internal] Compute the LU decomposition of a permuted matrix. + template + CImg& _LU(CImg& indx, bool& d) { + const int N = width(); + int imax = 0; + CImg vv(N); + indx.assign(N); + d = true; + cimg_forX(*this,i) { + Tfloat vmax = 0; + cimg_forX(*this,j) { + const Tfloat tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) { indx.fill(0); return fill(0); } + vv[i] = 1/vmax; + } + cimg_forX(*this,j) { + for (int i = 0; i=vmax) { vmax=tmp; imax=i; } + } + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d =!d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; + if (j + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + if (starting_node>=nb_nodes) + throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher than number of nodes %u.", + pixel_type(),starting_node,nb_nodes); + CImg dist(1,nb_nodes,1,1,cimg::type::max()); + dist(starting_node) = 0; + previous_node.assign(1,nb_nodes,1,1,(t)-1); + previous_node(starting_node) = (t)starting_node; + CImg Q(nb_nodes); + cimg_forX(Q,u) Q(u) = u; + cimg::swap(Q(starting_node),Q(0)); + unsigned int sizeQ = nb_nodes; + while (sizeQ) { + // Update neighbors from minimal vertex + const unsigned int umin = Q(0); + if (umin==ending_node) sizeQ = 0; + else { + const T dmin = dist(umin); + const T infty = cimg::type::max(); + for (unsigned int q = 1; qdist(Q(left))) || (rightdist(Q(right)));) { + if (right + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node=~0U) { + CImg foo; + return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + /** + \param starting_node Indice of the starting node. + \param ending_node Indice of the ending node. + \param previous_node 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. + \note image instance corresponds to the adjacency matrix of the graph. + **/ + template + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous_node) { + return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + template + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous_node) const { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "dijkstra(): Instance is not a graph adjacency matrix.", + cimg_instance); + + return dijkstra(*this,_width,starting_node,ending_node,previous_node); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { + return get_dijkstra(starting_node,ending_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { + CImg foo; + return get_dijkstra(starting_node,ending_node,foo); + } + + //! Return an image containing the ascii codes of the specified string. + /** + \param str input C-string to encode as an image. + \param is_last_zero Tells if the ending \c '0' character appear in the resulting image. + **/ + static CImg string(const char *const str, const bool is_last_zero=true) { + if (!str) return CImg(); + return CImg(str,std::strlen(str)+(is_last_zero?1:0)); + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg vector(const T& a0) { + _cimg_static CImg r(1,1); + r[0] = a0; + return r; + } + + //! Return a \c 1x2 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg vector(const T& a0, const T& a1) { + _cimg_static CImg r(1,2); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; + return r; + } + + //! Return a \c 1x3 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2) { + _cimg_static CImg r(1,3); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + return r; + } + + //! Return a \c 1x4 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { + _cimg_static CImg r(1,4); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a \c 1x5 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + _cimg_static CImg r(1,5); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + return r; + } + + //! Return a \c 1x6 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + _cimg_static CImg r(1,6); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + return r; + } + + //! Return a \c 1x7 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + _cimg_static CImg r(1,7); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; + return r; + } + + //! Return a \c 1x8 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + _cimg_static CImg r(1,8); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + return r; + } + + //! Return a \c 1x9 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + _cimg_static CImg r(1,9); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; + return r; + } + + //! Return a \c 1x10 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + _cimg_static CImg r(1,10); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; + return r; + } + + //! Return a \c 1x11 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + _cimg_static CImg r(1,11); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; + return r; + } + + //! Return a \c 1x12 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + _cimg_static CImg r(1,12); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + return r; + } + + //! Return a \c 1x13 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + _cimg_static CImg r(1,13); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; + return r; + } + + //! Return a \c 1x14 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + _cimg_static CImg r(1,14); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; + return r; + } + + //! Return a \c 1x15 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + _cimg_static CImg r(1,15); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + return r; + } + + //! Return a \c 1x16 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + _cimg_static CImg r(1,16); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 1x1 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg matrix(const T& a0) { + return vector(a0); + } + + //! Return a 2x2 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, + const T& a2, const T& a3) { + _cimg_static CImg r(2,2); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; + *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a 3x3 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + \param a4 Fifth matrix value. + \param a5 Sixth matrix value. + \param a6 Seventh matrix value. + \param a7 Eighth matrix value. + \param a8 Nineth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, const T& a2, + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { + _cimg_static CImg r(3,3); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; + return r; + } + + //! Return a 4x4 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + _cimg_static CImg r(4,4); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 5x5 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, + const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, + const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, + const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { + _cimg_static CImg r(5,5); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; + *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; + *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; + return r; + } + + //! Return a 1x1 symmetric matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg tensor(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 symmetric matrix tensor containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2) { + return matrix(a0,a1,a1,a2); + } + + //! Return a 3x3 symmetric matrix containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5); + } + + //! Return a 1x1 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1) { + return matrix(a0,0,0,a1); + } + + //! Return a 3x3 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2) { + return matrix(a0,0,0,0,a1,0,0,0,a2); + } + + //! Return a 4x4 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { + return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); + } + + //! Return a 5x5 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); + } + + //! Return a NxN identity matrix. + /** + \param N Dimension of the matrix. + **/ + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x) = 1; + return res; + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + /** + \param N Size of the resulting vector. + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + static CImg sequence(const unsigned int N, const T a0, const T a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or third quaternion coordinate. + \param w Angle of the rotation axis, or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion. + **/ + static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool is_quaternion=false) { + float X,Y,Z,W; + if (!is_quaternion) { + const float norm = (float)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)std::sin(nw/2), + cosa = (float)std::cos(nw/2); + X = nx*sina; + Y = ny*sina; + Z = nz*sina; + W = cosa; + } else { + const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w); + if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; } + else { X = Y = Z = 0; W = 1; } + } + const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W; + return CImg::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)), + (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)), + (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy))); + } + + //@} + //----------------------------------- + // + //! \name Value Manipulation + //@{ + //----------------------------------- + + //! Fill all pixel values with specified value. + /** + \param val Fill value. + **/ + CImg& fill(const T val) { + if (is_empty()) return *this; + if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val; + else std::memset(_data,(int)val,sizeof(T)*size()); + return *this; + } + + //! Fill all pixel values with specified value \newinstance. + CImg get_fill(const T val) const { + return CImg(_width,_height,_depth,_spectrum).fill(val); + } + + //! Fill sequentially all pixel values with specified values. + /** + \param val0 First fill value. + \param val1 Second fill value. + **/ + CImg& fill(const T val0, const T val1) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-1; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-2; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-3; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-4; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-5; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-6; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-7; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-8; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-9; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-10; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-11; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-12; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-13; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-14; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13,val14); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14, const T val15) { + if (is_empty()) return *this; + T *ptrd, *ptre = end()-15; + for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14, const T val15) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13,val14,val15); + } + + //! Fill sequentially pixel values according to a given expression. + /** + \param expression C-string describing a math formula, or a list of values. + \param repeat_flag In case a list of values is provided, tells if this list must be repeated for the filling. + **/ + CImg& fill(const char *const expression, const bool repeat_flag) { + if (is_empty() || !expression || !*expression) return *this; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { // Try to fill values according to a formula. + const CImg + _base = *expression!='>' && *expression!='<' && (std::strstr(expression,"i(") || std::strstr(expression,"j("))?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"fill"); + T *ptrd = *expression=='<'?end()-1:_data; + if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp.eval((double)x,(double)y,(double)z,(double)c); + else cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c); + } catch (CImgException& e) { // If failed, try to recognize a list of values. + char item[16384] = { 0 }, sep = 0; + const char *nexpression = expression; + unsigned long nb = 0; + const unsigned long siz = size(); + T *ptrd = _data; + for (double val = 0; *nexpression && nb0 && std::sscanf(item,"%lf",&val)==1) { + nexpression+=std::strlen(item) + (err>1?1:0); + *(ptrd++) = (T)val; + } else break; + } + cimg::exception_mode() = omode; + if (nb get_fill(const char *const values, const bool repeat_values) const { + return (+*this).fill(values,repeat_values); + } + + //! Fill sequentially pixel values according to the values found in another image. + /** + \param values Image containing the values used for the filling. + \param repeat_values In case there are less values than necessary in \c values, tells if these values must be repeated for the filling. + **/ + template + CImg& fill(const CImg& values, const bool repeat_values=true) { + if (is_empty() || !values) return *this; + T *ptrd = _data, *ptre = ptrd + size(); + for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs + CImg get_fill(const CImg& values, const bool repeat_values=true) const { + return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values); + } + + //! Fill pixel values along the X-axis at a specified pixel position. + /** + \param y Y-coordinate of the filled column. + \param z Z-coordinate of the filled column. + \param c C-coordinate of the filled column. + \param a0 First fill value. + **/ + CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) { +#define _cimg_fill1(x,y,z,c,off,siz,t) { \ + va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \ + for (unsigned long k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) { + if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position. + /** + \param x X-coordinate of the filled row. + \param z Z-coordinate of the filled row. + \param c C-coordinate of the filled row. + \param a0 First fill value. + **/ + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position \overloading. + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position. + /** + \param x X-coordinate of the filled slice. + \param y Y-coordinate of the filled slice. + \param c C-coordinate of the filled slice. + \param a0 First fill value. + **/ + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { + const unsigned long wh = (unsigned long)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position \overloading. + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { + const unsigned long wh = (unsigned long)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position. + /** + \param x X-coordinate of the filled channel. + \param y Y-coordinate of the filled channel. + \param z Z-coordinate of the filled channel. + \param a0 First filling value. + **/ + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { + const unsigned long whd = (unsigned long)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position \overloading. + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { + const unsigned long whd = (unsigned long)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double); + return *this; + } + + //! Discard specified value in the image buffer. + /** + \param value Value to discard. + \note Discarded values will change the image geometry, so the resulting image + is returned as a one-column vector. + **/ + CImg& discard(const T value) { + return get_discard(value).move_to(*this); + } + + //! Discard specified value in the image buffer \newinstance. + CImg get_discard(const T value) const { + CImg res(1,size()); + T *pd = res._data; + for (const T *ps = _data, *const pse = end(); ps(); + return res.resize(1,pd-res._data,1,1,-1); + } + + //! Discard specified sequence of values in the image buffer. + /** + \param values Sequence of values to discard. + \note Discarded values will change the image geometry, so the resulting image + is returned as a one-column vector. + **/ + template + CImg& discard(const CImg& values) { + return get_discard(values).move_to(*this); + } + + //! Discard specified sequence of values in the image buffer \newinstance. + template + CImg get_discard(const CImg& values) const { + if (!values) return *this; + if (values.size()==1) return get_discard(*values); + CImg res(1,size()); + T *pd = res._data; + const t *const pve = values.end(); + for (const T *ps = _data, *const pse = end(); ps(); + return res.resize(1,pd-res._data,1,1,-1); + } + + //! Invert endianness of all pixel values. + /** + **/ + CImg& invert_endianness() { + cimg::invert_endianness(_data,size()); + return *this; + } + + //! Invert endianness of all pixel values \newinstance. + CImg get_invert_endianness() const { + return (+*this).invert_endianness(); + } + + //! Fill image with random values in specified range. + /** + \param val_min Minimal random value. + \param val_max Maximal random value. + \note Random samples are following a uniform distribution. + **/ + CImg& rand(const T val_min, const T val_max) { + const float delta = (float)val_max - (float)val_min; + cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta); + return *this; + } + + //! Fill image with random values in specified range \newinstance. + CImg get_rand(const T val_min, const T val_max) const { + return (+*this).rand(val_min,val_max); + } + + //! Round pixel values. + /** + \param y Rounding precision. + \param rounding_type Rounding type. Can be: + - \c -1: Backward. + - \c 0: Nearest. + - \c 1: Forward. + **/ + CImg& round(const double y=1, const int rounding_type=0) { + if (y>0) cimg_for(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type); + return *this; + } + + //! Round pixel values \newinstance. + CImg get_round(const double y=1, const unsigned int rounding_type=0) const { + return (+*this).round(y,rounding_type); + } + + //! Add random noise to pixel values. + /** + \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the global value range. + \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, \p 3=Poisson or \p 4=Rician). + \return A reference to the modified image instance. + \note + - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on the image value itself. + - Function \p CImg::get_noise() is also defined. It returns a non-shared modified copy of the image instance. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_noise(40); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_noise.jpg + **/ + CImg& noise(const double sigma, const unsigned int noise_type=0) { + if (!is_empty()) { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0; + if (nsigma==0 && noise_type!=3) return *this; + if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M); + if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0); + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_for(*this,ptrd,T) { + Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand()); + if (val>vmax) val = vmax; + if (valvmax) val = vmax; + if (val::is_float()?1:cimg::type::max()); } + cimg_for(*this,ptrd,T) if (cimg::rand()*100vmax) val = vmax; + if (val get_noise(const double sigma, const unsigned int noise_type=0) const { + return (+*this).noise(sigma,noise_type); + } + + //! Linearly normalize pixel values. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(160,220); + (img,res).display(); + \endcode + \image html ref_normalize2.jpg + **/ + CImg& normalize(const T min_value, const T max_value) { + if (is_empty()) return *this; + const T a = min_value get_normalize(const T min_value, const T max_value) const { + return CImg(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value); + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. + /** + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_normalize.jpg + **/ + CImg& normalize() { + T *ptrd = _data; + const unsigned long whd = (unsigned long)_width*_height*_depth; + cimg_forXYZ(*this,x,y,z) { + const T *ptrs = ptrd; + float n = 0; + cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; } + n = (float)std::sqrt(n); + T *_ptrd = ptrd++; + if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; } + else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; } + } + return *this; + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance. + CImg get_normalize() const { + return CImg(*this,false).normalize(); + } + + //! Compute L2-norm of each multi-valued pixel of the image instance. + /** + \param norm_type Type of computed vector norm (can be \p 0=Linf, \p 1=L1 or \p 2=L2). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_norm(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_norm.jpg + **/ + CImg& norm(const int norm_type=2) { + if (_spectrum==1) return abs(); + return get_norm(norm_type).move_to(*this); + } + + //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance. + CImg get_norm(const int norm_type=2) const { + if (is_empty()) return *this; + if (_spectrum==1) return get_abs(); + const T *ptrs = _data; + const unsigned long whd = (unsigned long)_width*_height*_depth; + CImg res(_width,_height,_depth); + Tfloat *ptrd = res._data; + switch (norm_type) { + case -1 : { // Linf norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; } + *(ptrd++) = n; + } + } break; + case 1 : { // L1 norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } + *(ptrd++) = n; + } + } break; + default : { // L2 norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); + } + } + } + return res; + } + + //! Cut pixel values in specified range. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_cut(160,220); + (img,res).display(); + \endcode + \image html ref_cut.jpg + **/ + CImg& cut(const T min_value, const T max_value) { + if (is_empty()) return *this; + const T a = min_valueb)?b:*ptrd); + return *this; + } + + //! Cut pixel values in specified range \newinstance. + CImg get_cut(const T min_value, const T max_value) const { + return (+*this).cut(min_value,max_value); + } + + //! Uniformly quantize pixel values. + /** + \param nb_levels Number of quantization levels. + \param keep_range Tells if resulting values keep the same range as the original ones. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_quantize(4); + (img,res).display(); + \endcode + \image html ref_quantize.jpg + **/ + CImg& quantize(const unsigned int nb_levels, const bool keep_range=true) { + if (!nb_levels) + throw CImgArgumentException(_cimg_instance + "quantize(): Invalid quantization request with 0 values.", + cimg_instance); + + if (is_empty()) return *this; + Tfloat m, M = (Tfloat)max_min(m), range = M - m; + if (range>0) { + if (keep_range) cimg_for(*this,ptrd,T) { + const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); + *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels); + } else cimg_for(*this,ptrd,T) { + const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); + *ptrd = (T)cimg::min(val,nb_levels-1); + } + } + return *this; + } + + //! Uniformly quantize pixel values \newinstance. + CImg get_quantize(const unsigned int n, const bool keep_range=true) const { + return (+*this).quantize(n,keep_range); + } + + //! Threshold pixel values. + /** + \param value Threshold value + \param soft_threshold Tells if soft thresholding must be applied (instead of hard one). + \param strict_threshold Tells if threshold value is strict. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_threshold(128); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_threshold.jpg + **/ + CImg& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) { + if (is_empty()) return *this; + if (strict_threshold) { + if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; } + else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; + } else { + if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0; } + else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0; + } + return *this; + } + + //! Threshold pixel values \newinstance. + CImg get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const { + return (+*this).threshold(value,soft_threshold,strict_threshold); + } + + //! Compute the histogram of pixel values. + /** + \param nb_levels Number of desired histogram levels. + \param min_value Minimum pixel value considered for the histogram computation. All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. All pixel values higher than \p max_value will not be counted. + \note + - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x in the image I. + - If \p min_value==max_value==0 (default behavior), the function first estimates the whole range of pixel values + then uses it to compute the histogram. + - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional. + \par Example + \code + const CImg img = CImg("reference.jpg").histogram(256); + img.display_graph(0,3); + \endcode + \image html ref_histogram.jpg + **/ + CImg& histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) { + return get_histogram(nb_levels,min_value,max_value).move_to(*this); + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) const { + if (!nb_levels || is_empty()) return CImg(); + T vmin = min_value res(nb_levels,1,1,1,0); + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(unsigned int)((val-vmin)*nb_levels/(vmax-vmin))]; + } + return res; + } + + //! Equalize histogram of pixel values. + /** + \param nb_levels Number of histogram levels used for the equalization. + \param min_value Minimum pixel value considered for the histogram computation. All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. All pixel values higher than \p max_value will not be counted. + \note + - If \p min_value==max_value==0 (default behavior), the function first estimates the whole range of pixel values + then uses it to equalize the histogram. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_equalize(256); + (img,res).display(); + \endcode + \image html ref_equalize.jpg + **/ + CImg& equalize(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) { + if (is_empty()) return *this; + T vmin = min_value, vmax = max_value; + if (vmin==vmax && vmin==0) vmin = min_max(vmax); + if (vmin hist = get_histogram(nb_levels,vmin,vmax); + unsigned long cumul = 0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } + cimg_for(*this,ptrd,T) { + const int pos = (int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin)); + if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size()); + } + } + return *this; + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { + return (+*this).equalize(nblevels,val_min,val_max); + } + + //! Index multi-valued pixels regarding to a specified colormap. + /** + \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. + \param dithering Level of dithering (0=disable, 1=standard level). + \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors. + \note + - \p img.index(colormap,dithering,1) is equivalent to img.index(colormap,dithering,0).map(colormap). + \par Example + \code + const CImg img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); + const CImg res = img.get_index(colormap,1,true); + (img,res).display(); + \endcode + \image html ref_index.jpg + **/ + template + CImg& index(const CImg& colormap, const float dithering=1, const bool map_indexes=false) { + return get_index(colormap,dithering,map_indexes).move_to(*this); + } + + //! Index multi-valued pixels regarding to a specified colormap \newinstance. + template + CImg::Tuint> + get_index(const CImg& colormap, const float dithering=1, const bool map_indexes=true) const { + if (colormap._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + typedef typename CImg::Tuint tuint; + if (is_empty()) return CImg(); + const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth; + CImg res(_width,_height,_depth,map_indexes?_spectrum:1); + tuint *ptrd = res._data; + if (dithering>0) { // Dithered versions. + const float ndithering = (dithering<0?0:dithering>1?1:dithering)/16; + Tfloat valm = 0, valM = (Tfloat)max_min(valm); + if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; } + CImg cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1); + Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0); + const unsigned long cwhd = (unsigned long)cache._width*cache._height*cache._depth; + switch (_spectrum) { + case 1 : { // Optimized for scalars. + cimg_forYZ(*this,y,z) { + if (yvalM?valM:_val0; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1, + _val2 = (Tfloat)*ptrs2, val2 = _val2valM?valM:_val2; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrpvalM?valM:_val; + dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd; + } + if (dist::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp img("reference.jpg"), + colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), + colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), + res = img.get_index(colormap1,0).map(colormap2); + (img,res).display(); + \endcode + \image html ref_map.jpg + **/ + template + CImg& map(const CImg& colormap) { + return get_map(colormap).move_to(*this); + } + + //! Map predefined colormap on the scalar (indexed) image instance \newinstance. + template + CImg get_map(const CImg& colormap) const { + if (_spectrum!=1 && colormap._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "map(): Instance and specified colormap (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth; + CImg res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum); + switch (colormap._spectrum) { + case 1 : { // Optimized for scalars. + const T *ptrs = _data; + cimg_for(res,ptrd,t) { + const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) { + return get_label(is_high_connectivity,tolerance).move_to(*this); + } + + //! Label connected components \newinstance. + CImg get_label(const bool is_high_connectivity=false, + const Tfloat tolerance=0) const { + if (is_empty()) return CImg(); + + // Create neighborhood tables. + int dx[13], dy[13], dz[13], nb = 0; + dx[nb]=1; dy[nb] = 0; dz[nb++]=0; + dx[nb]=0; dy[nb] = 1; dz[nb++]=0; + if (is_high_connectivity) { + dx[nb]=1; dy[nb] = 1; dz[nb++]=0; + dx[nb]=1; dy[nb] = -1; dz[nb++]=0; + } + if (_depth>1) { // 3d version. + dx[nb]=0; dy[nb] = 0; dz[nb++]=1; + if (is_high_connectivity) { + dx[nb]=1; dy[nb] = 1; dz[nb++]=-1; + dx[nb]=1; dy[nb] = 0; dz[nb++]=-1; + dx[nb]=1; dy[nb] = -1; dz[nb++]=-1; + dx[nb]=0; dy[nb] = 1; dz[nb++]=-1; + + dx[nb]=0; dy[nb] = 1; dz[nb++]=1; + dx[nb]=1; dy[nb] = -1; dz[nb++]=1; + dx[nb]=1; dy[nb] = 0; dz[nb++]=1; + dx[nb]=1; dy[nb] = 1; dz[nb++]=1; + } + } + return _get_label(nb,dx,dy,dz,tolerance); + } + + //! Label connected components \overloading. + /** + \param connectivity_mask Mask of the neighboring pixels. + \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. + **/ + template + CImg& label(const CImg& connectivity_mask, const Tfloat tolerance=0) { + return get_label(connectivity_mask,tolerance).move_to(*this); + } + + //! Label connected components \newinstance. + template + CImg get_label(const CImg& connectivity_mask, + const Tfloat tolerance=0) const { + int nb = 0; + cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; + CImg dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0); + nb = 0; + cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && + connectivity_mask(x,y,z)) { + dx[nb] = x; dy[nb] = y; dz[nb++] = z; + } + return _get_label(nb,dx,dy,dz,tolerance); + } + + CImg _get_label(const unsigned int nb, const int + *const dx, const int *const dy, const int *const dz, + const Tfloat tolerance) const { + CImg res(_width,_height,_depth,_spectrum); + cimg_forC(*this,c) { + CImg _res = res.get_shared_channel(c); + + // Init label numbers. + unsigned long *ptr = _res.data(); + cimg_foroff(_res,p) *(ptr++) = p; + + // For each neighbour-direction, label. + for (unsigned int n = 0; n& _system_strescape() { +#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg(ptrs,p-ptrs,1,1,1,false).move_to(list); \ + CImg(s,std::strlen(s),1,1,1,false).move_to(list); ptrs = p+1; break + CImgList list; + const T *ptrs = _data; + cimg_for(*this,p,T) switch ((int)*p) { + cimg_system_strescape('\\',"\\\\"); + cimg_system_strescape('\"',"\\\""); + cimg_system_strescape('!',"\"\\!\""); + cimg_system_strescape('`',"\\`"); + cimg_system_strescape('$',"\\$"); + } + if (ptrs(ptrs,end()-ptrs,1,1,1,false).move_to(list); + return (list>'x').move_to(*this); + } + + //@} + //--------------------------------- + // + //! \name Color Base Management + //@{ + //--------------------------------- + + //! Return colormap \e "default", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_default.jpg + **/ + static const CImg& default_LUT256() { + static CImg colormap; + if (!colormap) { + colormap.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) { + colormap(0,index,0) = (Tuchar)r; + colormap(0,index,1) = (Tuchar)g; + colormap(0,index++,2) = (Tuchar)b; + } + } + return colormap; + } + + //! Return colormap \e "HSV", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hsv.jpg + **/ + static const CImg& HSV_LUT256() { + static CImg colormap; + if (!colormap) { + CImg tmp(1,256,1,3,1); + tmp.get_shared_channel(0).sequence(0,359); + colormap = tmp.HSVtoRGB(); + } + return colormap; + } + + //! Return colormap \e "lines", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_lines.jpg + **/ + static const CImg& lines_LUT256() { + 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 colormap(pal,1,256,1,3,false); + return colormap; + } + + //! Return colormap \e "hot", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hot.jpg + **/ + static const CImg& hot_LUT256() { + static CImg colormap; + if (!colormap) { + colormap.assign(1,4,1,3,0); + colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; + colormap.resize(1,256,1,3,3); + } + return colormap; + } + + //! Return colormap \e "cool", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cool.jpg + **/ + static const CImg& cool_LUT256() { + static CImg colormap; + if (!colormap) colormap.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3); + return colormap; + } + + //! Return colormap \e "jet", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_jet.jpg + **/ + static const CImg& jet_LUT256() { + static CImg colormap; + if (!colormap) { + colormap.assign(1,4,1,3,0); + colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; + colormap.resize(1,256,1,3,3); + } + return colormap; + } + + //! Return colormap \e "flag", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_flag.jpg + **/ + static const CImg& flag_LUT256() { + static CImg colormap; + if (!colormap) { + colormap.assign(1,4,1,3,0); + colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; + colormap.resize(1,256,1,3,0,2); + } + return colormap; + } + + //! Return colormap \e "cube", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cube.jpg + **/ + static const CImg& cube_LUT256() { + static CImg colormap; + if (!colormap) { + colormap.assign(1,8,1,3,0); + colormap[1] = colormap[3] = colormap[5] = colormap[7] = + colormap[10] = colormap[11] = colormap[12] = colormap[13] = + colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; + colormap.resize(1,256,1,3,3); + } + return colormap; + } + + //! Convert pixel values from sRGB to RGB color spaces. + CImg& sRGBtoRGB() { + cimg_for(*this,ptr,T) { + const Tfloat + sval = (Tfloat)*ptr, + nsval = (sval<0?0:sval>255?255:sval)/255, + val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f)); + *ptr = (T)(val*255); + } + return *this; + } + + //! Convert pixel values from sRGB to RGB color spaces \newinstance. + CImg get_sRGBtoRGB() const { + return CImg(*this,false).sRGBtoRGB(); + } + + //! Convert pixel values from RGB to sRGB color spaces. + CImg& RGBtosRGB() { + cimg_for(*this,ptr,T) { + const Tfloat + val = (Tfloat)*ptr, + nval = (val<0?0:val>255?255:val)/255, + sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f); + *ptr = (T)(sval*255); + } + return *this; + } + + //! Convert pixel values from RGB to sRGB color spaces \newinstance. + CImg get_RGBtosRGB() const { + return CImg(*this,false).RGBtosRGB(); + } + + //! Convert pixel values from RGB to HSV color spaces. + CImg& RGBtoHSV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to HSV color spaces \newinstance. + CImg get_RGBtoHSV() const { + return CImg(*this,false).RGBtoHSV(); + } + + //! Convert pixel values from HSV to RGB color spaces. + CImg& HSVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSVtoRGB(): Instance is not a HSV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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)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; + } + + //! Convert pixel values from HSV to RGB color spaces \newinstance. + CImg get_HSVtoRGB() const { + return CImg(*this,false).HSVtoRGB(); + } + + //! Convert pixel values from RGB to HSL color spaces. + CImg& RGBtoHSL() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSL(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to HSL color spaces \newinstance. + CImg get_RGBtoHSL() const { + return CImg< Tfloat>(*this,false).RGBtoHSL(); + } + + //! Convert pixel values from HSL to RGB color spaces. + CImg& HSLtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSLtoRGB(): Instance is not a HSL image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from HSL to RGB color spaces \newinstance. + CImg get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert pixel values from RGB to HSI color spaces. + CImg& RGBtoHSI() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSI(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI), + 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; + } + + //! Convert pixel values from RGB to HSI color spaces \newinstance. + CImg get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert pixel values from HSI to RGB color spaces. + CImg& HSItoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSItoRGB(): Instance is not a HSI image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180))); + G = 3*I-(R+B); + } else if (H<240) { + H-=120; + R = a; + G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180))); + B = 3*I-(R+G); + } else { + H-=240; + G = a; + B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/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; + } + + //! Convert pixel values from HSI to RGB color spaces \newinstance. + CImg get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& RGBtoYCbCr() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYCbCr(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to YCbCr color spaces \newinstance. + CImg get_RGBtoYCbCr() const { + return CImg(*this,false).RGBtoYCbCr(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& YCbCrtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YCbCrtoRGB(): Instance is not a YCbCr image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to YCbCr color spaces \newinstance. + CImg get_YCbCrtoRGB() const { + return CImg(*this,false).YCbCrtoRGB(); + } + + //! Convert pixel values from RGB to YUV color spaces. + CImg& RGBtoYUV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYUV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to YUV color spaces \newinstance. + CImg get_RGBtoYUV() const { + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert pixel values from YUV to RGB color spaces. + CImg& YUVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YUVtoRGB(): Instance is not a YUV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from YUV to RGB color spaces \newinstance. + CImg get_YUVtoRGB() const { + return CImg< Tuchar>(*this,false).YUVtoRGB(); + } + + //! Convert pixel values from RGB to CMY color spaces. + CImg& RGBtoCMY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoCMY(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { + const Tfloat + R = (Tfloat)*p1, + G = (Tfloat)*p2, + B = (Tfloat)*p3, + C = 255 - R, + M = 255 - G, + Y = 255 - B; + *(p1++) = (T)(C<0?0:(C>255?255:C)); + *(p2++) = (T)(M<0?0:(M>255?255:M)); + *(p3++) = (T)(Y<0?0:(Y>255?255:Y)); + } + return *this; + } + + //! Convert pixel values from RGB to CMY color spaces \newinstance. + CImg get_RGBtoCMY() const { + return CImg(*this,false).RGBtoCMY(); + } + + //! Convert pixel values from CMY to RGB color spaces. + CImg& CMYtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoRGB(): Instance is not a CMY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { + const Tfloat + C = (Tfloat)*p1, + M = (Tfloat)*p2, + Y = (Tfloat)*p3, + R = 255 - C, + G = 255 - M, + B = 255 - 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; + } + + //! Convert pixel values from CMY to RGB color spaces \newinstance. + CImg get_CMYtoRGB() const { + return CImg(*this,false).CMYtoRGB(); + } + + //! Convert pixel values from CMY to CMYK color spaces. + CImg& CMYtoCMYK() { + return get_CMYtoCMYK().move_to(*this); + } + + //! Convert pixel values from CMY to CMYK color spaces \newinstance. + CImg get_CMYtoCMYK() const { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoCMYK(): Instance is not a CMY image.", + cimg_instance); + + CImg res(_width,_height,_depth,4); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3); + for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { + Tfloat + C = (Tfloat)*(ps1++), + M = (Tfloat)*(ps2++), + Y = (Tfloat)*(ps3++), + K = cimg::min(C,M,Y); + if (K>=255) C = M = Y = 0; + else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } + *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C)); + *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M)); + *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y)); + *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K)); + } + return res; + } + + //! Convert pixel values from CMYK to CMY color spaces. + CImg& CMYKtoCMY() { + return get_CMYKtoCMY().move_to(*this); + } + + //! Convert pixel values from CMYK to CMY color spaces \newinstance. + CImg get_CMYKtoCMY() const { + if (_spectrum!=4) + throw CImgInstanceException(_cimg_instance + "CMYKtoCMY(): Instance is not a CMYK image.", + cimg_instance); + + CImg res(_width,_height,_depth,3); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); + for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { + const Tfloat + C = (Tfloat)*(ps1++), + M = (Tfloat)*(ps2++), + Y = (Tfloat)*(ps3++), + K = (Tfloat)*(ps4++), + K1 = 1 - K/255, + nC = C*K1 + K, + nM = M*K1 + K, + nY = Y*K1 + K; + *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC)); + *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM)); + *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY)); + } + return res; + } + + //! Convert pixel values from RGB to XYZ_709 color spaces. + /** + \note Uses the standard D65 white point. + **/ + CImg& RGBtoXYZ() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoXYZ(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from RGB to XYZ_709 color spaces \newinstance. + CImg get_RGBtoXYZ() const { + return CImg(*this,false).RGBtoXYZ(); + } + + //! Convert pixel values from XYZ_709 to RGB color spaces. + CImg& XYZtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoRGB(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from XYZ_709 to RGB color spaces \newinstance. + CImg get_XYZtoRGB() const { + return CImg(*this,false).XYZtoRGB(); + } + + //! Convert pixel values from XYZ_709 to Lab color spaces. + CImg& XYZtoLab() { +#define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116)) + + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoLab(): Instance is not a XYZ image.", + cimg_instance); + + 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 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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)cimg::max(0.0f,116*fY - 16); + *(p2++) = (T)(500*(fX - fY)); + *(p3++) = (T)(200*(fY - fZ)); + } + return *this; + } + + //! Convert pixel values from XYZ_709 to Lab color spaces \newinstance. + CImg get_XYZtoLab() const { + return CImg(*this,false).XYZtoLab(); + } + + //! Convert pixel values from Lab to XYZ_709 color spaces. + CImg& LabtoXYZ() { +#define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f)) + + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "LabtoXYZ(): Instance is not a Lab image.", + cimg_instance); + + 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 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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)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; + } + + //! Convert pixel values from Lab to XYZ_709 color spaces \newinstance. + CImg get_LabtoXYZ() const { + return CImg(*this,false).LabtoXYZ(); + } + + //! Convert pixel values from XYZ_709 to xyY color spaces. + CImg& XYZtoxyY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoxyY(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from XYZ_709 to xyY color spaces \newinstance. + CImg get_XYZtoxyY() const { + return CImg(*this,false).XYZtoxyY(); + } + + //! Convert pixel values from xyY pixels to XYZ_709 color spaces. + CImg& xyYtoXYZ() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "xyYtoXYZ(): Instance is not a xyY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + for (unsigned long N = (unsigned long)_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; + } + + //! Convert pixel values from xyY pixels to XYZ_709 color spaces \newinstance. + CImg get_xyYtoXYZ() const { + return CImg(*this,false).xyYtoXYZ(); + } + + //! Convert pixel values from RGB to Lab color spaces. + CImg& RGBtoLab() { + return RGBtoXYZ().XYZtoLab(); + } + + //! Convert pixel values from RGB to Lab color spaces \newinstance. + CImg get_RGBtoLab() const { + return CImg(*this,false).RGBtoLab(); + } + + //! Convert pixel values from Lab to RGB color spaces. + CImg& LabtoRGB() { + return LabtoXYZ().XYZtoRGB(); + } + + //! Convert pixel values from Lab to RGB color spaces \newinstance. + CImg get_LabtoRGB() const { + return CImg(*this,false).LabtoRGB(); + } + + //! Convert pixel values from RGB to xyY color spaces. + CImg& RGBtoxyY() { + return RGBtoXYZ().XYZtoxyY(); + } + + //! Convert pixel values from RGB to xyY color spaces \newinstance. + CImg get_RGBtoxyY() const { + return CImg(*this,false).RGBtoxyY(); + } + + //! Convert pixel values from xyY to RGB color spaces. + CImg& xyYtoRGB() { + return xyYtoXYZ().XYZtoRGB(); + } + + //! Convert pixel values from xyY to RGB color spaces \newinstance. + CImg get_xyYtoRGB() const { + return CImg(*this,false).xyYtoRGB(); + } + + //! Convert pixel values from RGB to CMYK color spaces. + CImg& RGBtoCMYK() { + return RGBtoCMY().CMYtoCMYK(); + } + + //! Convert pixel values from RGB to CMYK color spaces \newinstance. + CImg get_RGBtoCMYK() const { + return CImg(*this,false).RGBtoCMYK(); + } + + //! Convert pixel values from CMYK to RGB color spaces. + CImg& CMYKtoRGB() { + return CMYKtoCMY().CMYtoRGB(); + } + + //! Convert pixel values from CMYK to RGB color spaces \newinstance. + CImg get_CMYKtoRGB() const { + return CImg(*this,false).CMYKtoRGB(); + } + + //! Convert RGB color image to a Bayer-coded scalar image. + /** + \note First (upper-left) pixel if the red component of the pixel color. + **/ + CImg& RGBtoBayer() { + return get_RGBtoBayer().move_to(*this); + } + + //! Convert RGB color image to a Bayer-coded scalar image \newinstance. + CImg get_RGBtoBayer() const { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoBayer(): Instance is not a RGB image.", + cimg_instance); + + CImg res(_width,_height,_depth,1); + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + T *ptrd = res._data; + cimg_forXYZ(*this,x,y,z) { + if (y%2) { + if (x%2) *(ptrd++) = *ptr_b; + else *(ptrd++) = *ptr_g; + } else { + if (x%2) *(ptrd++) = *ptr_g; + else *(ptrd++) = *ptr_r; + } + ++ptr_r; ++ptr_g; ++ptr_b; + } + return res; + } + + //! Convert Bayer-coded scalar image to a RGB color image. + CImg& BayertoRGB(const unsigned int interpolation_type=3) { + return get_BayertoRGB(interpolation_type).move_to(*this); + } + + //! Convert Bayer-coded scalar image to a RGB color image \newinstance. + CImg get_BayertoRGB(const unsigned int interpolation_type=3) const { + if (_spectrum!=1) + throw CImgInstanceException(_cimg_instance + "BayertoRGB(): Instance is not a Bayer image.", + cimg_instance); + + CImg res(_width,_height,_depth,3); + CImg_3x3(I,T); + Tuchar *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + 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=2) return 0; + const float a = (float)cimg::PI*x, b = 0.5f*a; + return (float)(x?std::sin(a)*std::sin(b)/(a*b):1); + } + + //! Resize image to new dimensions. + /** + \param size_x Number of columns (new size along the X-axis). + \param size_y Number of rows (new size along the Y-axis). + \param size_z Number of slices (new size along the Z-axis). + \param size_c Number of vector-channels (new size along the C-axis). + \param interpolation_type Method of interpolation: + - -1 = no interpolation: raw memory resizing. + - 0 = no interpolation: additional space is filled according to \p boundary_conditions. + - 1 = nearest-neighbor interpolation. + - 2 = moving average interpolation. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = cubic interpolation. + - 6 = lanczos interpolation. + \param boundary_conditions Border condition type. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int size_x, const int size_y=-100, + const int size_z=-100, const int size_c=-100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + if (!size_x || !size_y || !size_z || !size_c) return assign(); + const unsigned int + _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), + _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), + _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), + _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), + sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; + if (is_empty()) return assign(sx,sy,sz,sc,0); + if (interpolation_type==-1 && sx*sy*sz*sc==size()) { + _width = sx; _height = sy; _depth = sz; _spectrum = sc; + return *this; + } + return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*this); + } + + //! Resize image to new dimensions \newinstance. + CImg get_resize(const int size_x, const int size_y = -100, + const int size_z = -100, const int size_c = -100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 || + centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1) + throw CImgArgumentException(_cimg_instance + "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].", + cimg_instance, + centering_x,centering_y,centering_z,centering_c); + + if (!size_x || !size_y || !size_z || !size_c) return CImg(); + const unsigned int + _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), + _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), + _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), + _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), + sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this; + if (is_empty()) return CImg(sx,sy,sz,sc,0); + + CImg res; + switch (interpolation_type) { + + // Raw resizing. + // + case -1 : + std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc)); + break; + + // No interpolation. + // + case 0 : { + const int + xc = (int)(centering_x*((int)sx - width())), + yc = (int)(centering_y*((int)sy - height())), + zc = (int)(centering_z*((int)sz - depth())), + cc = (int)(centering_c*((int)sc - spectrum())); + + switch (boundary_conditions) { + case 2 : { // Cyclic borders. + res.assign(sx,sy,sz,sc); + const int + x0 = ((int)xc%width()) - width(), + y0 = ((int)yc%height()) - height(), + z0 = ((int)zc%depth()) - depth(), + c0 = ((int)cc%spectrum()) - spectrum(); + for (int c = c0; c<(int)sc; c+=spectrum()) + for (int z = z0; z<(int)sz; z+=depth()) + for (int y = y0; y<(int)sy; y+=height()) + for (int x = x0; x<(int)sx; x+=width()) + res.draw_image(x,y,z,c,*this); + } break; + case 1 : { // Neumann borders. + res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this); + CImg sprite; + if (xc>0) { // X-backward + res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite); + for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite); + } + if (xc+width()<(int)sx) { // X-forward + res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite); + for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite); + } + if (yc>0) { // Y-backward + res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite); + for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite); + } + if (yc+height()<(int)sy) { // Y-forward + res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite); + for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite); + } + if (zc>0) { // Z-backward + res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite); + for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite); + } + if (zc+depth()<(int)sz) { // Z-forward + res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite); + for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite); + } + if (cc>0) { // C-backward + res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite); + for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite); + } + if (cc+spectrum()<(int)sc) { // C-forward + res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite); + for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); + } + } break; + default : // Dirichlet borders. + res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this); + } + break; + } break; + + // Nearest neighbor interpolation. + // + case 1 : { + res.assign(sx,sy,sz,sc); + CImg off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1); + unsigned long *poff_x, *poff_y, *poff_z, *poff_c, curr, old; + const unsigned long + wh = (unsigned long)_width*_height, + whd = (unsigned long)_width*_height*_depth, + sxy = (unsigned long)sx*sy, + sxyz = (unsigned long)sx*sy*sz; + if (sx==_width) off_x.fill(1); + else { + poff_x = off_x._data; curr = 0; + cimg_forX(res,x) { old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; } + } + if (sy==_height) off_y.fill(_width); + else { + poff_y = off_y._data; curr = 0; + cimg_forY(res,y) { old = curr; curr = ((y+1LU)*_height/sy); *(poff_y++) = _width*(curr - old); } *poff_y = 0; + } + if (sz==_depth) off_z.fill(wh); + else { + poff_z = off_z._data; curr = 0; + cimg_forZ(res,z) { old = curr; curr = ((z+1LU)*_depth/sz); *(poff_z++) = wh*(curr - old); } *poff_z = 0; + } + if (sc==_spectrum) off_c.fill(whd); + else { + poff_c = off_c._data; curr = 0; + cimg_forC(res,c) { old = curr; curr = ((c+1LU)*_spectrum/sc); *(poff_c++) = whd*(curr - old); } *poff_c = 0; + } + + T *ptrd = res._data; + const T* ptrv = _data; + poff_c = off_c._data; + for (unsigned int c = 0; c tmp(sx,_height,_depth,_spectrum,0); + for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; ++t; b = _width; } + if (!c) { ++s; c = sx; } + } + tmp.move_to(res); + instance_first = false; + } + if (sy!=_height) { + CImg tmp(sx,sy,_depth,_spectrum,0); + for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; ++t; b = _height; } + if (!c) { ++s; c = sy; } + } + tmp.move_to(res); + instance_first = false; + } + if (sz!=_depth) { + CImg tmp(sx,sy,sz,_spectrum,0); + for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; ++t; b = _depth; } + if (!c) { ++s; c = sz; } + } + tmp.move_to(res); + instance_first = false; + } + if (sc!=_spectrum) { + CImg tmp(sx,sy,sz,sc,0); + for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, 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)/=_spectrum; ++t; b = _spectrum; } + if (!c) { ++s; c = sc; } + } + tmp.move_to(res); + instance_first = false; + } + } break; + + // Linear interpolation. + // + case 3 : { + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + unsigned int *poff; + float *pfoff, old, curr; + CImg resx, resy, resz, resc; + T *ptrd; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } + ptrd = resx._data; + const T *ptrs0 = _data; + cimg_forYZC(resx,y,z,c) { + poff = off._data; pfoff = foff._data; + const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1); + cimg_forX(resx,x) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } + cimg_forXZC(resy,x,z,c) { + ptrd = resy.data(x,0,z,c); + const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx; + poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } + cimg_forXYC(resz,x,y,c) { + ptrd = resz.data(x,y,0,c); + const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy; + poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } + cimg_forXYZ(resc,x,y,z) { + ptrd = resc.data(x,y,z,0); + const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz; + poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrs resx, resy, resz, resc; + if (sx!=_width) { + if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + resx.assign(sx,_height,_depth,_spectrum,0); + const int dx = sx*2, dy = width()*2; + int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0; + cimg_forX(resx,x) if ((err-=dy)<=0) { + cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c); + ++xs; + err+=dx; + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + resy.assign(sx,sy,_depth,_spectrum,0); + const int dx = sy*2, dy = height()*2; + int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0; + cimg_forY(resy,y) if ((err-=dy)<=0) { + cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c); + ++ys; + err+=dx; + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + resz.assign(sx,sy,sz,_spectrum,0); + const int dx = sz*2, dy = depth()*2; + int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0; + cimg_forZ(resz,z) if ((err-=dy)<=0) { + cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c); + ++zs; + err+=dx; + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + resc.assign(sx,sy,sz,sc,0); + const int dx = sc*2, dy = spectrum()*2; + int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0; + cimg_forC(resc,c) if ((err-=dy)<=0) { + cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs); + ++cs; + err+=dx; + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Cubic interpolation. + // + case 5 : { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + unsigned int *poff; + float *pfoff, old, curr; + CImg resx, resy, resz, resc; + T *ptrd; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } + ptrd = resx._data; + const T *ptrs0 = _data; + cimg_forYZC(resx,y,z,c) { + poff = off._data; pfoff = foff._data; + const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2); + cimg_forX(resx,x) { + const float t = *(pfoff++); + const Tfloat + val1 = (Tfloat)*ptrs, + val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1, + val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1, + val3 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + ptrs0+=_width; + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } + cimg_forXZC(resy,x,z,c) { + ptrd = resy.data(x,0,z,c); + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx; + poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { + const float t = *(pfoff++); + const Tfloat + val1 = (Tfloat)*ptrs, + val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1, + val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } + cimg_forXYC(resz,x,y,c) { + ptrd = resz.data(x,y,0,c); + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy; + poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { + const float t = *(pfoff++); + const Tfloat + val1 = (Tfloat)*ptrs, + val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1, + val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } + cimg_forXYZ(resc,x,y,z) { + ptrd = resc.data(x,y,z,0); + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz; + poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { + const float t = *(pfoff++); + const Tfloat + val1 = (Tfloat)*ptrs, + val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1, + val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Lanczos interpolation. + // + case 6 : { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + unsigned int *poff; + float *pfoff, old, curr; + CImg resx, resy, resz, resc; + T *ptrd; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } + ptrd = resx._data; + const T *ptrs0 = _data; + cimg_forYZC(resx,y,z,c) { + poff = off._data; pfoff = foff._data; + const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2); + cimg_forX(resx,x) { + const float + t = *(pfoff++), + w0 = _cimg_lanczos(t+2), + w1 = _cimg_lanczos(t+1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t-1), + w4 = _cimg_lanczos(t-2); + const Tfloat + val2 = (Tfloat)*ptrs, + val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2, + val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1, + val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2, + val4 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + ptrs0+=_width; + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } + cimg_forXZC(resy,x,z,c) { + ptrd = resy.data(x,0,z,c); + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx; + poff = off._data; pfoff = foff._data; + cimg_forY(resy,y) { + const float + t = *(pfoff++), + w0 = _cimg_lanczos(t+2), + w1 = _cimg_lanczos(t+1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t-1), + w4 = _cimg_lanczos(t-2); + const Tfloat + val2 = (Tfloat)*ptrs, + val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2, + val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1, + val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } + cimg_forXYC(resz,x,y,c) { + ptrd = resz.data(x,y,0,c); + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy; + poff = off._data; pfoff = foff._data; + cimg_forZ(resz,z) { + const float + t = *(pfoff++), + w0 = _cimg_lanczos(t+2), + w1 = _cimg_lanczos(t+1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t-1), + w4 = _cimg_lanczos(t-2); + const Tfloat + val2 = (Tfloat)*ptrs, + val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2, + val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1, + val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } + cimg_forXYZ(resc,x,y,z) { + ptrd = resc.data(x,y,z,0); + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz; + poff = off._data; pfoff = foff._data; + cimg_forC(resc,c) { + const float + t = *(pfoff++), + w0 = _cimg_lanczos(t+2), + w1 = _cimg_lanczos(t+1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t-1), + w4 = _cimg_lanczos(t-2); + const Tfloat + val2 = (Tfloat)*ptrs, + val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2, + val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1, + val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Unknow interpolation. + // + default : + throw CImgArgumentException(_cimg_instance + "resize(): Invalid specified interpolation %d " + "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=cubic | 6=lanczos }).", + cimg_instance, + interpolation_type); + } + return res; + } + + //! Resize image to dimensions of another image. + /** + \param src Reference image used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + template + CImg& resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of another image \newinstance. + template + CImg get_resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window. + /** + \param disp Reference display window used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + CImg& resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window \newinstance. + CImg get_resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to half-size along XY axes, using an optimized filter. + CImg& resize_halfXY() { + return get_resize_halfXY().move_to(*this); + } + + //! Resize image to half-size along XY axes, using an optimized filter \newinstance. + CImg get_resize_halfXY() const { + if (is_empty()) return *this; + const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + T I[9] = { 0 }; + CImg res(_width/2,_height/2,_depth,_spectrum); + T *ptrd = res._data; + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) + if (x%2 && y%2) *(ptrd++) = (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 res; + } + + //! Resize image to double-size, using the Scale2X algorithm. + /** + \note Use anisotropic upscaling algorithm described here. + **/ + CImg& resize_doubleXY() { + return get_resize_doubleXY().move_to(*this); + } + + //! Resize image to double-size, using the Scale2X algorithm \newinstance. + CImg get_resize_doubleXY() const { +#define _cimg_gs2x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) + +#define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs2x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(_width<<1,_height<<1,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width; + _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) { + 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; + } + + //! Resize image to triple-size, using the Scale3X algorithm. + /** + \note Use anisotropic upscaling algorithm described here. + **/ + CImg& resize_tripleXY() { + return get_resize_tripleXY().move_to(*this); + } + + //! Resize image to triple-size, using the Scale3X algorithm \newinstance. + CImg get_resize_tripleXY() const { +#define _cimg_gs3x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width) + +#define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs3x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width()-1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(3*_width,3*_height,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width, + *ptrd3 = ptrd2 + res._width; + _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) { + 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; + } + + //! Mirror image content along specified axis. + /** + \param axis Mirror axis + **/ + CImg& mirror(const char axis) { + if (is_empty()) return *this; + T *pf, *pb, *buf = 0; + switch (cimg::uncase(axis)) { + case 'x' : { + pf = _data; pb = data(_width-1); + const unsigned int width2 = _width/2; + for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) { + for (unsigned int x = 0; x get_mirror(const char axis) const { + return (+*this).mirror(axis); + } + + //! Mirror image content along specified axes. + /** + \param axes Mirror axes, as a C-string. + \note \c axes may contains multiple character, e.g. \c "xyz" + **/ + CImg& mirror(const char *const axes) { + for (const char *s = axes; *s; s++) mirror(*s); + return *this; + } + + //! Mirror image content along specified axes \newinstance. + CImg get_mirror(const char *const axes) const { + return (+*this).mirror(axes); + } + + //! Shift image content. + /** + \param delta_x Amount of displacement along the X-axis. + \param delta_y Amount of displacement along the Y-axis. + \param delta_z Amount of displacement along the Z-axis. + \param delta_c Amount of displacement along the C-axis. + \param boundary_conditions Border condition. + + - \c boundary_conditions can be: + - 0: Zero border condition (Dirichlet). + - 1: Nearest neighbors (Neumann). + - 2: Repeat Pattern (Fourier style). + **/ + CImg& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const int boundary_conditions=0) { + if (is_empty()) return *this; + if (delta_x) // Shift along X-axis + switch (boundary_conditions) { + case 0 : + if (cimg::abs(delta_x)>=width()) return fill(0); + if (delta_x<0) cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width+delta_x)*sizeof(T)); + std::memset(data(_width+delta_x,y,z,c),0,-delta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T)); + std::memset(data(0,y,z,c),0,delta_x*sizeof(T)); + } + break; + case 1 : + if (delta_x<0) { + const int ndelta_x = (-delta_x>=width())?width()-1:-delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(_width-1,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width())?width()-1:delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(0,y,z,c); + const T val = *ptrd; + for (int l = 0; l0) cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T)); + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(_width+ndelta_x,y,z,c),-ndelta_x*sizeof(T)); + std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width+ndelta_x)*sizeof(T)); + std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T)); + } + delete[] buf; + } + } + + if (delta_y) // Shift along Y-axis + switch (boundary_conditions) { + case 0 : + if (cimg::abs(delta_y)>=height()) return fill(0); + if (delta_y<0) cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height+delta_y)*sizeof(T)); + std::memset(data(0,_height+delta_y,z,c),0,-delta_y*_width*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T)); + std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T)); + } + break; + case 1 : + if (delta_y<0) { + const int ndelta_y = (-delta_y>=height())?height()-1:-delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height-1,z,c); + for (int l = 0; l=height())?height()-1:delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c); + for (int l = 0; l0) cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T)); + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,_height+ndelta_y,z,c),-ndelta_y*_width*sizeof(T)); + std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height+ndelta_y)*sizeof(T)); + std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T)); + } + delete[] buf; + } + } + + if (delta_z) // Shift along Z-axis + switch (boundary_conditions) { + case 0 : + if (cimg::abs(delta_z)>=depth()) return fill(0); + if (delta_z<0) cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth+delta_z)*sizeof(T)); + std::memset(data(0,0,_depth+delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); + } else cimg_forC(*this,c) { + std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T)); + std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T)); + } + break; + case 1 : + if (delta_z<0) { + const int ndelta_z = (-delta_z>=depth())?depth()-1:-delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth-1,c); + for (int l = 0; l=depth())?depth()-1:delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c); + for (int l = 0; l0) cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T)); + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T)); + } else cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,_depth+ndelta_z,c),-ndelta_z*_width*_height*sizeof(T)); + std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth+ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T)); + } + delete[] buf; + } + } + + if (delta_c) // Shift along C-axis + switch (boundary_conditions) { + case 0 : + if (cimg::abs(delta_c)>=spectrum()) return fill(0); + if (delta_c<0) { + std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum+delta_c)*sizeof(T)); + std::memset(data(0,0,0,_spectrum+delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); + } else { + std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T)); + std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T)); + } + break; + case 1 : + if (delta_c<0) { + const int ndelta_c = (-delta_c>=spectrum())?spectrum()-1:-delta_c; + if (!ndelta_c) return *this; + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum-1); + for (int l = 0; l=spectrum())?spectrum()-1:delta_c; + if (!ndelta_c) return *this; + std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,1); + for (int l = 0; l0) { + std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T)); + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T)); + } else { + std::memcpy(buf,data(0,0,0,_spectrum+ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T)); + std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum+ndelta_c)*sizeof(T)); + std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T)); + } + delete[] buf; + } + } + return *this; + } + + //! Shift image content \newinstance. + CImg get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const int boundary_conditions=0) const { + return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions); + } + + //! Permute axes order. + /** + \param order Axes permutations, as a C-string of 4 characters. + This function permutes image content regarding the specified axes permutation. + **/ + CImg& permute_axes(const char *const order) { + return get_permute_axes(order).move_to(*this); + } + + //! Permute axes order \newinstance. + CImg get_permute_axes(const char *const order) const { + const T foo = (T)0; + return _get_permute_axes(order,foo); + } + + template + CImg _get_permute_axes(const char *const permut, const t&) const { + if (is_empty() || !permut) return CImg(*this,false); + CImg res; + const T* ptrs = _data; + if (!cimg::strncasecmp(permut,"xyzc",4)) return +*this; + if (!cimg::strncasecmp(permut,"xycz",4)) { + res.assign(_width,_height,_spectrum,_depth); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xzyc",4)) { + res.assign(_width,_depth,_height,_spectrum); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xzcy",4)) { + res.assign(_width,_depth,_spectrum,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xcyz",4)) { + res.assign(_width,_spectrum,_height,_depth); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xczy",4)) { + res.assign(_width,_spectrum,_depth,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yxzc",4)) { + res.assign(_height,_width,_depth,_spectrum); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yxcz",4)) { + res.assign(_height,_width,_spectrum,_depth); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yzxc",4)) { + res.assign(_height,_depth,_width,_spectrum); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yzcx",4)) { + res.assign(_height,_depth,_spectrum,_width); + switch (_width) { + case 1 : { + t *ptr_r = res.data(0,0,0,0); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); + } + } break; + case 2 : { + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); + } + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); + } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++); + } + } break; + default : { + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); + return res; + } + } + } + if (!cimg::strncasecmp(permut,"ycxz",4)) { + res.assign(_height,_spectrum,_width,_depth); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yczx",4)) { + res.assign(_height,_spectrum,_depth,_width); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zxyc",4)) { + res.assign(_depth,_width,_height,_spectrum); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zxcy",4)) { + res.assign(_depth,_width,_spectrum,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zyxc",4)) { + res.assign(_depth,_height,_width,_spectrum); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zycx",4)) { + res.assign(_depth,_height,_spectrum,_width); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zcxy",4)) { + res.assign(_depth,_spectrum,_width,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zcyx",4)) { + res.assign(_depth,_spectrum,_height,_width); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"cxyz",4)) { + res.assign(_spectrum,_width,_height,_depth); + switch (_spectrum) { + case 1 : { + const T *ptr_r = data(0,0,0,0); + t *ptrd = res._data; + for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); + } break; + case 2 : { + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); + t *ptrd = res._data; + for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) { + *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + t *ptrd = res._data; + for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) { + *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + t *ptrd = res._data; + for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) { + *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++); + } + } break; + default : { + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } + } + } + if (!cimg::strncasecmp(permut,"cxzy",4)) { + res.assign(_spectrum,_width,_depth,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"cyxz",4)) { + res.assign(_spectrum,_height,_width,_depth); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"cyzx",4)) { + res.assign(_spectrum,_height,_depth,_width); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"czxy",4)) { + res.assign(_spectrum,_depth,_width,_height); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"czyx",4)) { + res.assign(_spectrum,_depth,_height,_width); + const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); + } + if (!res) + throw CImgArgumentException(_cimg_instance + "permute_axes(): Invalid specified permutation '%s'.", + cimg_instance, + permut); + return res; + } + + //! Unroll pixel values along specified axis. + /** + \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c'). + **/ + CImg& unroll(const char axis) { + const unsigned int siz = size(); + if (siz) switch (axis) { + case 'x' : _width = siz; _height = _depth = _spectrum = 1; break; + case 'y' : _height = siz; _width = _depth = _spectrum = 1; break; + case 'z' : _depth = siz; _width = _height = _spectrum = 1; break; + default : _spectrum = siz; _width = _height = _depth = 1; + } + return *this; + } + + //! Unroll pixel values along specified axis \newinstance. + CImg get_unroll(const char axis) const { + return (+*this).unroll(axis); + } + + //! Rotate image with arbitrary angle. + /** + \param angle Rotation angle, in degrees. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=cyclic }. + \note Most of the time, size of the image is modified. + **/ + CImg& rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0) { + return get_rotate(angle,interpolation,boundary).move_to(*this); + } + + //! Rotate image with arbitrary angle \newinstance. + CImg get_rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0) const { + if (is_empty()) return *this; + CImg res; + const float nangle = cimg::mod(angle,360.0f); + if (boundary!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles. + const int wm1 = width() - 1, hm1 = height() - 1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { // 90 deg. + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c); + } break; + case 2 : { // 180 deg. + res.assign(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c); + } break; + case 3 : { // 270 deg. + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c); + } break; + default : // 0 deg. + return *this; + } + } else { // Generic angle. + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + const float + rad = (float)(nangle*cimg::PI/180.0), + ca = (float)std::cos(rad), + sa = (float)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); + res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum); + switch (boundary) { + case 0 : { // Dirichlet boundaries. + switch (interpolation) { + case 2 : { // Cubic interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { // Linear interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0); + } break; + default : { // Nearest-neighbor interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c,0); + } + } + } break; + case 1 : { // Neumann boundaries. + switch (interpolation) { + case 2 : { // Cubic interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { // Linear interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c); + } break; + default : { // Nearest-neighbor interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c); + } + } + } break; + case 2 : { // Cyclic boundaries. + switch (interpolation) { + case 2 : { // Cubic interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()), + cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { // Linear interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()), + cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c); + } break; + default : { // Nearest-neighbor interpolation. + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()), + cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c); + } + } + } break; + default : + throw CImgArgumentException(_cimg_instance + "rotate(): Invalid specified border conditions %d " + "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).", + cimg_instance, + boundary); + } + } + return res; + } + + //! Rotate image with arbitrary angle, around a center point. + /** + \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 factor. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=cyclic }. + \param interpolation_type Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + **/ + CImg& rotate(const float angle, const float cx, const float cy, const float zoom, + const unsigned int interpolation=1, const unsigned int boundary=3) { + return get_rotate(angle,cx,cy,zoom,interpolation,boundary).move_to(*this); + } + + //! Rotate image with arbitrary angle, around a center point \newinstance. + CImg get_rotate(const float angle, const float cx, const float cy, const float zoom, + const unsigned int interpolation=1, const unsigned int boundary=3) const { + if (interpolation>2) + throw CImgArgumentException(_cimg_instance + "rotate(): Invalid specified interpolation type %d " + "(should be { 0=none | 1=linear | 2=cubic }).", + cimg_instance, + interpolation); + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + const float + rad = (float)((angle*cimg::PI)/180.0), + ca = (float)std::cos(rad)/zoom, + sa = (float)std::sin(rad)/zoom; + switch (boundary) { + case 0 : { + switch (interpolation) { + case 2 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0); + } break; + default : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c,0); + } + } + } break; + case 1 : { + switch (interpolation) { + case 2 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c); + } break; + default : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c); + } + } + } break; + case 2 : { + switch (interpolation) { + case 2 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { + const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()), + cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c); + res(x,y,z,c) = (T)(valvmax?vmax:val); + } + } break; + case 1 : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()), + cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c); + } break; + default : { + cimg_forXY(res,x,y) cimg_forZC(*this,z,c) + res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()), + cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c); + } + } + } break; + default : + throw CImgArgumentException(_cimg_instance + "rotate(): Invalid specified border conditions %d " + "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).", + cimg_instance, + boundary); + } + return res; + } + + //! Warp image content by a warping field. + /** + \param warp Warping field. + \param is_relative Tells if warping field gives absolute or relative warping coordinates. + \param interpolation Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=cyclic }. + **/ + template + CImg& warp(const CImg& warp, const bool is_relative=false, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + return get_warp(warp,is_relative,interpolation,boundary_conditions).move_to(*this); + } + + //! Warp image content by a warping field \newinstance + template + CImg get_warp(const CImg& warp, const bool is_relative=false, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty() || !warp) return *this; + if (is_relative && !is_sameXYZ(warp)) + throw CImgArgumentException(_cimg_instance + "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) " + "have different XYZ dimensions.", + cimg_instance, + warp._width,warp._height,warp._depth,warp._spectrum,warp._data); + + CImg res(warp._width,warp._height,warp._depth,_spectrum); + T *ptrd = res._data; + + if (warp._spectrum==1) { // 1d warping. + if (is_relative) { // Relative warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atX(x - (float)*(ptrs0++),y,z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atX(x - (float)*(ptrs0++),y,z,c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0); + } + } else { // Nearest-neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0); + } + } + } else { // Absolute warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atX((float)*(ptrs0++),0,0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atX((float)*(ptrs0++),0,0,c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0); + } + } else { // Nearest-neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atX((int)*(ptrs0++),0,0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp._data; + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0); + } + } + } + + } else if (warp._spectrum==2) { // 2d warping. + if (is_relative) { // Relative warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), + cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), + cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0); + } + } else { // Nearest-neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width), + cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0); + } + } + } else { // Absolute warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXY(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height),0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height),0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0); + } + } else { // Nearest-neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width), + cimg::mod((int)*(ptrs1++),(int)_height),0,c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0); + } + } + } + + } else if (warp._spectrum==3) { // 3d warping. + if (is_relative) { // Relative warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width), + cimg::mod(y - (float)*(ptrs1++),(float)_height), + cimg::mod(z - (float)*(ptrs2++),(float)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width), + cimg::mod(y - (float)*(ptrs1++),(float)_height), + cimg::mod(z - (float)*(ptrs2++),(float)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0); + } + } else { // Nearest neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width), + cimg::mod(y - (int)*(ptrs1++),(int)_height), + cimg::mod(z - (int)*(ptrs2++),(int)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0); + } + } + } else { // Absolute warp. + if (interpolation==2) { // Cubic interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height), + cimg::mod((float)*(ptrs2++),(float)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0); + } + } else if (interpolation==1) { // Linear interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height), + cimg::mod((float)*(ptrs2++),(float)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0); + } + } else { // Nearest-neighbor interpolation. + if (boundary_conditions==2) // Cyclic boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width), + cimg::mod((int)*(ptrs1++),(int)_height), + cimg::mod((int)*(ptrs2++),(int)_depth),c); + } + else if (boundary_conditions==1) // Neumann boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c); + } + else // Dirichlet boundaries. + cimg_forC(res,c) { + const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); + cimg_forXYZ(res,x,y,z) + *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0); + } + } + } + } + return res; + } + + //! Generate a 2d representation of a 3d image, with XY,XZ and YZ views. + /** + \param x0 X-coordinate of the projection point. + \param y0 Y-coordinate of the projection point. + \param z0 Z-coordinate of the projection point. + **/ + CImg get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const { + if (is_empty() || _depth<2) return +*this; + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + const CImg + img_xy = get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1), + img_zy = get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).permute_axes("xzyc").resize(_depth,_height,1,-100,-1), + img_xz = get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1); + return CImg(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())). + draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy). + draw_image(0,img_xy._height,img_xz); + } + + //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace. + CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) { + if (_depth<2) return *this; + return get_projections2d(x0,y0,z0).move_to(*this); + } + + //! Crop image region. + /** + \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 c0 = C-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 c1 = C-coordinate of the lower-right crop rectangle corner. + \param boundary_conditions = Dirichlet (false) or Neumann border conditions. + **/ + CImg& crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const bool boundary_conditions=false) { + return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const bool boundary_conditions=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "crop(): Empty instance.", + cimg_instance); + const int + nx0 = x0 res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0); + if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) { + if (boundary_conditions) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c); + else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); + } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); + return res; + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const bool boundary_conditions=false) { + return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const bool boundary_conditions=false) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, + const int x1, const int y1, + const bool boundary_conditions=false) { + return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, + const int x1, const int y1, + const bool boundary_conditions=false) const { + return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int x1, const bool boundary_conditions=false) { + return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int x1, const bool boundary_conditions=false) const { + return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions); + } + + //! Autocrop image region, regarding the specified background value. + CImg& autocrop(const T value, const char *const axes="czyx") { + if (is_empty()) return *this; + for (const char *s = axes; *s; ++s) { + const char axis = cimg::uncase(*s); + const CImg coords = _autocrop(value,axis); + if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels. + else switch (axis) { + case 'x' : { + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) crop(x0,x1); + } break; + case 'y' : { + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1); + } break; + case 'z' : { + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1); + } break; + default : { + const int c0 = coords[0], c1 = coords[1]; + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background value \newinstance. + CImg get_autocrop(const T value, const char *const axes="czyx") const { + return (+*this).autocrop(value,axes); + } + + //! Autocrop image region, regarding the specified background color. + /** + \param color Color used for the crop. If \c 0, color is guessed. + \param axes Axes used for the crop. + **/ + CImg& autocrop(const T *const color=0, const char *const axes="zyx") { + if (is_empty()) return *this; + if (!color) { // Guess color. + const CImg col1 = get_vector_at(0,0,0); + const unsigned int w = _width, h = _height, d = _depth, s = _spectrum; + autocrop(col1,axes); + if (_width==w && _height==h && _depth==d && _spectrum==s) { + const CImg col2 = get_vector_at(w-1,h-1,d-1); + autocrop(col2,axes); + } + return *this; + } + for (const char *s = axes; *s; ++s) { + const char axis = cimg::uncase(*s); + switch (axis) { + case 'x' : { + int x0 = width(), x1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'x'); + 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==width() && x1==-1) return assign(); else crop(x0,x1); + } break; + case 'y' : { + int y0 = height(), y1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'y'); + 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==height() && y1==-1) return assign(); else crop(0,y0,_width-1,y1); + } break; + default : { + int z0 = depth(), z1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'z'); + 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==depth() && z1==-1) return assign(); else crop(0,0,z0,_width-1,_height-1,z1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background color \newinstance. + CImg get_autocrop(const T *const color=0, const char *const axes="zyx") const { + return (+*this).autocrop(color,axes); + } + + //! Autocrop image region, regarding the specified background color \overloading. + template CImg& autocrop(const CImg& color, const char *const axes="zyx") { + return get_autocrop(color,axes).move_to(*this); + } + + //! Autocrop image region, regarding the specified background color \newinstance. + template CImg get_autocrop(const CImg& color, const char *const axes="zyx") const { + return get_autocrop(color._data,axes); + } + + CImg _autocrop(const T value, const char axis) const { + CImg res; + switch (cimg::uncase(axis)) { + case 'x' : { + int x0 = -1, x1 = -1; + cimg_forX(*this,x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); } + if (x0>=0) { + for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } + } + res = CImg::vector(x0,x1); + } break; + case 'y' : { + int y0 = -1, y1 = -1; + cimg_forY(*this,y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); } + if (y0>=0) { + for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } + } + res = CImg::vector(y0,y1); + } break; + case 'z' : { + int z0 = -1, z1 = -1; + cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); } + if (z0>=0) { + for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } + } + res = CImg::vector(z0,z1); + } break; + default : { + int c0 = -1, c1 = -1; + cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); } + if (c0>=0) { + for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } + } + res = CImg::vector(c0,c1); + } + } + return res; + } + + //! Return specified image column. + /** + \param x0 Image column. + **/ + CImg get_column(const int x0) const { + return get_columns(x0,x0); + } + + //! Return specified image column \inplace. + CImg& column(const int x0) { + return columns(x0,x0); + } + + //! Return specified range of image columns. + /** + \param x0 Starting image column. + \param x1 Ending image column. + **/ + CImg& columns(const int x0, const int x1) { + return get_columns(x0,x1).move_to(*this); + } + + //! Return specified range of image columns \inplace. + CImg get_columns(const int x0, const int x1) const { + return get_crop(x0,0,0,0,x1,height()-1,depth()-1,spectrum()-1); + } + + //! Return specified image row. + CImg get_row(const int y0) const { + return get_rows(y0,y0); + } + + //! Return specified image row \inplace. + /** + \param y0 Image row. + **/ + CImg& row(const int y0) { + return rows(y0,y0); + } + + //! Return specified range of image rows. + /** + \param y0 Starting image row. + \param y1 Ending image row. + **/ + CImg get_rows(const int y0, const int y1) const { + return get_crop(0,y0,0,0,width()-1,y1,depth()-1,spectrum()-1); + } + + //! Return specified range of image rows \inplace. + CImg& rows(const int y0, const int y1) { + return get_rows(y0,y1).move_to(*this); + } + + //! Return specified image slice. + /** + \param z0 Image slice. + **/ + CImg get_slice(const int z0) const { + return get_slices(z0,z0); + } + + //! Return specified image slice \inplace. + CImg& slice(const int z0) { + return slices(z0,z0); + } + + //! Return specified range of image slices. + /** + \param z0 Starting image slice. + \param z1 Ending image slice. + **/ + CImg get_slices(const int z0, const int z1) const { + return get_crop(0,0,z0,0,width()-1,height()-1,z1,spectrum()-1); + } + + //! Return specified range of image slices \inplace. + CImg& slices(const int z0, const int z1) { + return get_slices(z0,z1).move_to(*this); + } + + //! Return specified image channel. + /** + \param c0 Image channel. + **/ + CImg get_channel(const int c0) const { + return get_channels(c0,c0); + } + + //! Return specified image channel \inplace. + CImg& channel(const int c0) { + return channels(c0,c0); + } + + //! Return specified range of image channels. + /** + \param c0 Starting image channel. + \param c1 Ending image channel. + **/ + CImg get_channels(const int c0, const int c1) const { + return get_crop(0,0,0,c0,width()-1,height()-1,depth()-1,c1); + } + + //! Return specified range of image channels \inplace. + CImg& channels(const int c0, const int c1) { + return get_channels(c0,c1).move_to(*this); + } + + //! Return stream line of a 2d or 3d vector field. + CImg get_streamline(const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false) const { + if (_spectrum!=2 && _spectrum!=3) + throw CImgInstanceException(_cimg_instance + "streamline(): Instance is not a 2d or 3d vector field.", + cimg_instance); + if (_spectrum==2) { + if (is_oriented_only) { + typename CImg::_functor4d_streamline2d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,0.0f); + } else { + typename CImg::_functor4d_streamline2d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,0.0f); + } + } + if (is_oriented_only) { + typename CImg::_functor4d_streamline3d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f); + } + typename CImg::_functor4d_streamline3d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f); + } + + //! Return stream line of a 3d vector field. + /** + \param func Vector field function. + \param x X-coordinate of the starting point of the streamline. + \param y Y-coordinate of the starting point of the streamline. + \param z Z-coordinate of the starting point of the streamline. + \param L Streamline length. + \param dl Streamline length increment. + \param interpolation_type Type of interpolation. Can be { 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }. + \param is_backward_tracking Tells if the streamline is estimated forward or backward. + \param is_oriented_only Tells if the direction of the vectors must be ignored. + \param x0 X-coordinate of the first bounding-box vertex. + \param y0 Y-coordinate of the first bounding-box vertex. + \param z0 Z-coordinate of the first bounding-box vertex. + \param x1 X-coordinate of the second bounding-box vertex. + \param y1 Y-coordinate of the second bounding-box vertex. + \param z1 Z-coordinate of the second bounding-box vertex. + **/ + template + static CImg streamline(const tfunc& func, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + if (dl<=0) + throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g " + "(should be >0).", + pixel_type(), + dl); + + const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1); + if (L<=0 || (is_bounded && (xx1 || yy1 || zz1))) return CImg(); + const unsigned int size_L = (unsigned int)cimg::round(L/dl+1); + CImg coordinates(size_L,3); + const float dl2 = dl/2; + float + *ptr_x = coordinates.data(0,0), + *ptr_y = coordinates.data(0,1), + *ptr_z = coordinates.data(0,2), + pu = (float)(dl*func(x,y,z,0)), + pv = (float)(dl*func(x,y,z,1)), + pw = (float)(dl*func(x,y,z,2)), + X = x, Y = y, Z = z; + + switch (interpolation_type) { + case 0 : { // Nearest integer interpolation. + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + const int + xi = (int)(X>0?X+0.5f:X-0.5f), + yi = (int)(Y>0?Y+0.5f:Y-0.5f), + zi = (int)(Z>0?Z+0.5f:Z-0.5f); + float + u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)), + v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)), + w = (float)(dl*func((float)xi,(float)yi,(float)zi,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 1 : { // First-order interpolation. + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u = (float)(dl*func(X,Y,Z,0)), + v = (float)(dl*func(X,Y,Z,1)), + w = (float)(dl*func(X,Y,Z,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 2 : { // Second order interpolation. + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)), + v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)), + w = (float)(dl*func(X+u0,Y+v0,Z+w0,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + default : { // Fourth order interpolation. + cimg_forX(coordinates,x) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)), + v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)), + w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2)); + if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; } + float + u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)), + v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)), + w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; } + float + u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)), + v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)), + w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; } + const float + u = (u0 + u3)/3 + (u1 + u2)/1.5f, + v = (v0 + v3)/3 + (v1 + v2)/1.5f, + w = (w0 + w3)/3 + (w1 + w2)/1.5f; + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } + } + if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0); + return coordinates; + } + + //! Return stream line of a 3d vector field \overloading. + static CImg streamline(const char *const expression, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=true, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + _functor4d_streamline_expr func(expression); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1); + } + + struct _functor4d_streamline2d_directed { + const CImg& ref; + _functor4d_streamline2d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0; + } + }; + + struct _functor4d_streamline3d_directed { + const CImg& ref; + _functor4d_streamline3d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref._linear_atXYZ(x,y,z,c); + } + }; + + struct _functor4d_streamline2d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,1,2); } + ~_functor4d_streamline2d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign2d(i,j) if (I(i,j,0)*I(0,0,0)+I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z; + const float + dx = x - xi, + dy = y - yi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1; + if (yi<0) yi = 0; if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1; + I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1); + I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1); + I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1); + I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1); + _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1); + } + return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0; + } + }; + + struct _functor4d_streamline3d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,2,3); } + ~_functor4d_streamline3d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0)+I(i,j,k,1)*I(0,0,0,1)+I(i,j,k,2)*I(0,0,0,2)<0) { \ + I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z - (z>=0?0:1), nzi = zi + 1; + const float + dx = x - xi, + dy = y - yi, + dz = z - zi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1; + if (yi<0) yi = 0; if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1; + if (zi<0) zi = 0; if (nzi<0) nzi = 0; + if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1; + I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); I(0,0,0,2) = (float)ref(xi,yi,zi,2); + I(1,0,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2); + I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); + I(0,1,0,0) = (float)ref(xi,nyi,zi,0); I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,yi,zi,2); + I(0,0,0,1) = (float)ref(xi,yi,nzi,0); I(0,0,0,1) = (float)ref(xi,yi,nzi,1); I(0,0,0,2) = (float)ref(xi,yi,nzi,2); + I(1,0,0,1) = (float)ref(nxi,yi,nzi,0); I(1,0,0,1) = (float)ref(nxi,yi,nzi,1); I(1,0,0,2) = (float)ref(nxi,yi,nzi,2); + I(1,1,0,1) = (float)ref(nxi,nyi,nzi,0); I(1,1,0,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,0,2) = (float)ref(nxi,nyi,nzi,2); + I(0,1,0,1) = (float)ref(xi,nyi,nzi,0); I(0,1,0,1) = (float)ref(xi,nyi,nzi,1); I(0,1,0,2) = (float)ref(xi,yi,nzi,2); + _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0); + _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1); + } + return (float)pI->_linear_atXYZ(dx,dy,dz,c); + } + }; + + struct _functor4d_streamline_expr { + _cimg_math_parser *mp; + ~_functor4d_streamline_expr() { delete mp; } + _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,"streamline"); } + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)mp->eval(x,y,z,c); + } + }; + + //! Return a shared-memory image referencing a range of pixels of the image instance. + /** + \param x0 X-coordinate of the starting pixel. + \param x1 X-coordinate of the ending pixel. + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) { + const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + + return CImg(_data+beg,x1-x0+1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of pixels of the image instance \const. + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const { + const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + + return CImg(_data+beg,x1-x0+1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance. + /** + \param y0 Y-coordinate of the starting row. + \param y1 Y-coordinate of the ending row. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) { + const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).", + cimg_instance, + _width-1,y0,y1,z0,c0); + + return CImg(_data+beg,_width,y1-y0+1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance \const. + const CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) const { + const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).", + cimg_instance, + _width-1,y0,y1,z0,c0); + + return CImg(_data+beg,_width,y1-y0+1,1,1,true); + } + + //! Return a shared-memory image referencing one row of the image instance. + /** + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared-memory image referencing one row of the image instance \const. + const CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared memory image referencing a range of slices of the image instance. + /** + \param z0 Z-coordinate of the starting slice. + \param z1 Z-coordinate of the ending slice. + \param c0 C-coordinate. + **/ + CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { + const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width-1,_height-1,z0,z1,c0); + + return CImg(_data+beg,_width,_height,z1-z0+1,1,true); + } + + //! Return a shared memory image referencing a range of slices of the image instance \const. + const CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { + const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width-1,_height-1,z0,z1,c0); + + return CImg(_data+beg,_width,_height,z1-z0+1,1,true); + } + + //! Return a shared-memory image referencing one slice of the image instance. + /** + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing one slice of the image instance \const. + const CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) const { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing a range of channels of the image instance. + /** + \param c0 C-coordinate of the starting channel. + \param c1 C-coordinate of the ending channel. + **/ + CImg get_shared_channels(const unsigned int c0, const unsigned int c1) { + const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width-1,_height-1,_depth-1,c0,c1); + + return CImg(_data+beg,_width,_height,_depth,c1-c0+1,true); + } + + //! Return a shared-memory image referencing a range of channels of the image instance \const. + const CImg get_shared_channels(const unsigned int c0, const unsigned int c1) const { + const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width-1,_height-1,_depth-1,c0,c1); + + return CImg(_data+beg,_width,_height,_depth,c1-c0+1,true); + } + + //! Return a shared-memory image referencing one channel of the image instance. + /** + \param c0 C-coordinate. + **/ + CImg get_shared_channel(const unsigned int c0) { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory image referencing one channel of the image instance \const. + const CImg get_shared_channel(const unsigned int c0) const { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory version of the image instance. + CImg get_shared() { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Return a shared-memory version of the image instance \const. + const CImg get_shared() const { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Split image into a list along specified axis. + /** + \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param nb Number of splitted parts. + \note + - If \c nb==0, there are as much splitted parts as the image size along the specified axis. + - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide. + - If \c nb>0, instance image is splitted into \c nb blocs. + **/ + CImgList get_split(const char axis, const int nb=0) const { + CImgList res; + const char _axis = cimg::uncase(axis); + + if (nb<=0) { // Split by bloc size. + const unsigned int dp = (unsigned int)(nb?-nb:1); + int p = 0; + switch (_axis) { + case 'x': { + for (int pe=_width-dp; psiz) + throw CImgArgumentException(_cimg_instance + "get_split(): Instance cannot be split along %c-axis into %u blocs.", + cimg_instance, + axis,nb); + int err = (int)siz; + unsigned int _p = 0; + switch (_axis) { + case 'x' : { + cimg_forX(*this,p) if ((err-=nb)<=0) { + get_crop(_p,0,0,0,p,_height-1,_depth-1,_spectrum-1).move_to(res); + err+=(int)siz; + _p=p+1; + } + } break; + case 'y' : { + cimg_forY(*this,p) if ((err-=nb)<=0) { + get_crop(0,_p,0,0,_width-1,p,_depth-1,_spectrum-1).move_to(res); + err+=(int)siz; + _p=p+1; + } + } break; + case 'z' : { + cimg_forZ(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,_p,0,_width-1,_height-1,p,_spectrum-1).move_to(res); + err+=(int)siz; + _p=p+1; + } + } break; + default : { + cimg_forC(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,0,_p,_width-1,_height-1,_depth-1,p).move_to(res); + err+=(int)siz; + _p=p+1; + } + } + } + } + return res; + } + + //! Split image into a list of one-column vectors, according to a specified splitting value. + /** + \param value Splitting value. + \param keep_values Tells if the splitting value must be kept in the splitted blocs. + \param is_shared Tells if the splitted blocs have shared memory buffers. + **/ + CImgList get_split(const T value, const bool keep_values, const bool is_shared) const { + CImgList res; + for (const T *ps = _data, *_ps = ps, *const pe = end(); ps(ps,1,siz,1,1,is_shared),~0U,is_shared); + ps = _ps; + while (_ps(ps,1,siz,1,1,is_shared),~0U,is_shared); + ps = _ps; + } + return res; + } + + //! Split image into a list of one-column vectors, according to a specified splitting value sequence. + /** + \param values Splitting value sequence. + \param keep_values Tells if the splitting sequence must be kept in the splitted blocs. + \param is_shared Tells if the splitted blocs have shared memory buffers. + **/ + template + CImgList get_split(const CImg& values, const bool keep_values, const bool is_shared) const { + if (!values) return CImgList(*this); + if (values.size()==1) return get_split(*values,keep_values,is_shared); + CImgList res; + const t *pve = values.end(); + for (const T *ps = _data, *_ps = ps, *const pe = end(); ps(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found. + ps = _ps; + + // Try to find non-match from current position. + do { + pv = values._data; + while (_ps(ps,1,siz,1,1,is_shared),~0U,is_shared); + ps = _ps; + } + return res; + } + + //! Append two images along specified axis. + /** + \param img Image to append with instance image. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Append alignment in \c [0,1]. + **/ + template + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,true).insert(img).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \specialization. + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,img,true).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \const. + template + CImg<_cimg_Tt> get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); + } + + //! Append two images along specified axis \specialization. + CImg get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList(*this,img,true).get_append(axis,align); + } + + //@} + //--------------------------------------- + // + //! \name Filtering / Transforms + //@{ + //--------------------------------------- + + //! Correlate image by a mask. + /** + \param mask = the correlation kernel. + \param boundary_conditions = the border condition type (0=zero, 1=dirichlet) + \param is_normalized = enable local normalization. + \note + - The correlation of the image instance \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). + **/ + template + CImg& correlate(const CImg& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) { + if (is_empty() || !mask) return *this; + return get_correlate(mask,boundary_conditions,is_normalized).move_to(*this); + } + + //! Correlate image by a mask \newinstance. + template + CImg<_cimg_Ttfloat> get_correlate(const CImg& mask, const unsigned int boundary_conditions=1, + const bool is_normalized=false) const { + if (is_empty() || !mask) return *this; + typedef _cimg_Ttfloat Ttfloat; + CImg res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum)); + if (boundary_conditions && 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 boundary_conditions=1) + Ttfloat *ptrd = res._data; + switch (mask._depth) { + case 3 : { + T I[27] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_for3x3x3(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + + 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]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + + 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])/std::sqrt(N):0); + } + } else cimg_for3x3x3(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + + 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]); + } + } break; + case 2 : { + T I[8] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_for2x2x2(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3] + + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7]); + *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + + I[2]*_mask[2] + I[3]*_mask[3] + + I[4]*_mask[4] + I[5]*_mask[5] + + I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0); + } + } else cimg_for2x2x2(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + + I[2]*_mask[2] + I[3]*_mask[3] + + I[4]*_mask[4] + I[5]*_mask[5] + + I[6]*_mask[6] + I[7]*_mask[7]); + } + } break; + default : + case 1 : + switch (mask._width) { + case 6 : { + T I[36] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + + 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]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + + 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])/std::sqrt(N):0); + } + } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + + 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]); + } + } break; + case 5 : { + T I[25] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + + I[ 5]*I[ 5] + 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]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + + I[ 5]*_mask[ 5] + 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])/std::sqrt(N):0); + } + } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + + I[ 5]*_mask[ 5] + 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]); + } + } break; + case 4 : { + T I[16] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + + I[ 4]*I[ 4] + I[ 5]*I[ 5] + 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]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + 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])/std::sqrt(N):0); + } + } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + 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]); + } + } break; + case 3 : { + T I[9] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + + I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); + *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] + + I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] + + I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0); + } + } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] + + I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] + + I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]); + } + } break; + case 2 : { + T I[4] = { 0 }; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3]); + *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + + I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0); + } + } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + + I[2]*_mask[2] + I[3]*_mask[3]); + } + } break; + case 1 : + if (is_normalized) res.fill(1); + else cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + res.get_shared_channel(c).assign(_img)*=_mask[0]; + } + break; + } + } + } else { // Generic version for other masks and borders conditions. + const int + mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, + mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + cimg_forC(res,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { // Normalized correlation. + const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0, N = 0; + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const Ttfloat _val = (Ttfloat)_img._atXYZ(x+xm,y+ym,z+zm); + val+=_val*_mask(mx1+xm,my1+ym,mz1+zm); + N+=_val*_val; + } + N*=M; + res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); + } + else + cimg_forYZ(res,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0, N = 0; + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const Ttfloat _val = (Ttfloat)_img.atXYZ(x+xm,y+ym,z+zm,0,0); + val+=_val*_mask(mx1+xm,my1+ym,mz1+zm); + N+=_val*_val; + } + N*=M; + res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); + } + } else { // Classical correlation. + for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0; + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + val+=_img._atXYZ(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm); + res(x,y,z,c) = (Ttfloat)val; + } + else + cimg_forYZ(res,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0; + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + val+=_img.atXYZ(x+xm,y+ym,z+zm,0,0)*_mask(mx1+xm,my1+ym,mz1+zm); + res(x,y,z,c) = (Ttfloat)val; + } + } + } + } + return res; + } + + //! Convolve image by a mask. + /** + \param mask = the correlation kernel. + \param boundary_conditions = the border condition type (0=zero, 1=dirichlet) + \param is_normalized = enable local normalization. + \note + - 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) + **/ + template + CImg& convolve(const CImg& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) { + if (is_empty() || !mask) return *this; + return get_convolve(mask,boundary_conditions,is_normalized).move_to(*this); + } + + //! Convolve image by a mask \newinstance. + template + CImg<_cimg_Ttfloat> get_convolve(const CImg& mask, const unsigned int boundary_conditions=1, + const bool is_normalized=false) const { + if (is_empty() || !mask) return *this; + return get_correlate(CImg(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),boundary_conditions,is_normalized); + } + + //! Erode image by a structuring element. + /** + \param mask Structuring element. + \param boundary_conditions Boundary conditions. + \param is_normalized Tells if the erosion is locally normalized. + **/ + template + CImg& erode(const CImg& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) { + if (is_empty() || !mask) return *this; + return get_erode(mask,boundary_conditions,is_normalized).move_to(*this); + } + + //! Erode image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_erode(const CImg& mask, const unsigned int boundary_conditions=1, + const bool is_normalized=false) const { + if (is_empty() || !mask) return *this; + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum)); + const int + mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, + mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + cimg_forC(*this,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { // Normalized erosion. + for (int z = mz1; z::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval); + if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval); + if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval); + if (mval && cval::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)_img(x+xm,y+ym,z+zm); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis. + const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forYZC(*this,y,z,c) { + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _height>1) { // Along Y-axis. + const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forXZC(*this,x,z,c) { + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _depth>1) { // Along Z-axis. + const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forXYC(*this,x,y,c) { + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).erode(sx,sy,sz); + } + + //! Erode the image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& erode(const unsigned int s) { + return erode(s,s,s); + } + + //! Erode the image by a square structuring element of specified size \newinstance. + CImg get_erode(const unsigned int s) const { + return (+*this).erode(s); + } + + //! Dilate image by a structuring element. + /** + \param mask Structuring element. + \param boundary_conditions Boundary conditions. + \param is_normalized Tells if the erosion is locally normalized. + **/ + template + CImg& dilate(const CImg& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) { + if (is_empty() || !mask) return *this; + return get_dilate(mask,boundary_conditions,is_normalized).move_to(*this); + } + + //! Dilate image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_dilate(const CImg& mask, const unsigned int boundary_conditions=1, + const bool is_normalized=false) const { + if (is_empty() || !mask) return *this; + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,_spectrum); + const int + mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, + mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + cimg_forC(*this,c) { + const CImg _img = get_shared_channel(c%_spectrum); + const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + if (is_normalized) { // Normalized dilation. + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval); + if (mval && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + if (boundary_conditions) + cimg_forYZ(res,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval); + if (mval && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + else + cimg_forYZ(*this,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = _mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval); + if (mval && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } else { // Classical dilation. + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const Tt cval = (Tt)_img(x+xm,y+ym,z+zm); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + if (boundary_conditions) + cimg_forYZ(res,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + else + cimg_forYZ(res,y,z) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0); + if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } + } + return res; + } + + //! Dilate image by a rectangular structuring element of specified size. + /** + \param sx Width of the structuring element. + \param sy Height of the structuring element. + \param sz Depth of the structuring element. + **/ + CImg& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis. + const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forYZC(*this,y,z,c) { + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } *(ptrd++) = cur; } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur; + for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; } + T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + + if (sy>1 && _height>1) { // Along Y-axis. + const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forXZC(*this,x,z,c) { + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } *(ptrd++) = cur; } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur; + for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; } + T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + + if (sz>1 && _depth>1) { // Along Z-axis. + const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + cimg_forXYC(*this,x,y,c) { + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur; + for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } *(ptrd++) = cur; } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur; + for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; } + T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + return *this; + } + + //! Dilate image by a rectangular structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).dilate(sx,sy,sz); + } + + //! Dilate image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& dilate(const unsigned int s) { + return dilate(s,s,s); + } + + //! Dilate image by a square structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int s) const { + return (+*this).dilate(s); + } + + //! Compute watershed transform. + /** + \param priority Priority map. + \param fill_lines Tells if watershed lines must be filled or not. + \note Non-zero values of the instance instance are propagated to zero-valued ones according to specified the priority map. + **/ + template + CImg& watershed(const CImg& priority, const bool fill_lines=true) { + if (is_empty()) return *this; + if (!is_sameXYZ(priority)) + throw CImgArgumentException(_cimg_instance + "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance,priority._width,priority._height,priority._depth,priority._spectrum,priority._data); + if (_spectrum!=1) { cimg_forC(*this,c) get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines); return *this; } + + CImg in_queue(_width,_height,_depth,1,0); + CImg::type> Q; + unsigned int sizeQ = 0; + + // Find seed points and insert them in priority queue. + const T *ptrs = _data; + cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { + if (x-1>=0 && !(*this)(x-1,y,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); + if (x+1=0 && !(*this)(x,y-1,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); + if (y+1=0 && !(*this)(x,y,z-1)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); + if (z+1=0) { + if ((*this)(x-1,y,z)) { if (!label) label = (unsigned int)(*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; } + else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); + } + if (x+1=0) { + if ((*this)(x,y-1,z)) { if (!label) label = (unsigned int)(*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; } + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); + } + if (y+1=0) { + if ((*this)(x,y,z-1)) { if (!label) label = (unsigned int)(*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; } + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); + } + if (z+1=0 && (*this)(x-1,y,z)) || (x+1=0 && (*this)(x,y-1,z)) || (y+1=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1)))) + Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z),x,y,z); + + // Start line filling process. + while (sizeQ) { + const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); + Q._priority_queue_remove(sizeQ); + t pmax = cimg::type::min(); + int xmax = 0, ymax = 0, zmax = 0; + if (x-1>=0) { + if ((*this)(x-1,y,z)) { if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); + } + if (x+1pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z); + } + if (y-1>=0) { + if ((*this)(x,y-1,z)) { if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); + } + if (y+1pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z); + } + if (z-1>=0) { + if ((*this)(x,y,z-1)) { if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); + } + if (z+1pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }} + else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1); + } + (*this)(x,y,z) = (*this)(xmax,ymax,zmax); + } + } + return *this; + } + + //! Compute watershed transform \newinstance. + template + CImg get_watershed(const CImg& priority, const bool fill_lines=true) const { + return (+*this).watershed(priority,fill_lines); + } + + // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. + template + bool _priority_queue_insert(CImg& in_queue, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) { + if (in_queue(x,y,z)) return false; + in_queue(x,y,z) = true; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3)); + } + return true; + } + + CImg& _priority_queue_remove(unsigned int& siz) { + (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1); (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3); + const float value = (*this)(0,0); + for (unsigned int pos = 0, left = 0, right = 0; + ((right=2*(pos+1),(left=right-1))(*this)(right,0)) { + cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1)); + cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3)); + pos = left; + } else { + cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1)); + cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3)); + pos = right; + } + } else { + cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1)); + cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3)); + pos = left; + } + } + return *this; + } + + //! Apply recursive Deriche filter. + /** + \param sigma Standard deviation of the filter. + \param order Order of the filter. Can be { 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + **/ + CImg& deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) { +#define _cimg_deriche_apply \ + Tfloat *ptrY = Y._data, yb = 0, yp = 0; \ + T xp = (T)0; \ + if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ + for (int m = 0; m=0; --n) { \ + const T xc = *(ptrX-=off); \ + const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \ + xa = xn; xn = xc; ya = yn; yn = yc; \ + *ptrX = (T)(*(--ptrY)+yc); \ + } + const char naxis = cimg::uncase(axis); + const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.1f && !order)) return *this; + const float + nnsigma = nsigma<0.1f?0.1f:nsigma, + alpha = 1.695f/nnsigma, + ema = (float)std::exp(-alpha), + ema2 = (float)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)*(1-ema)/(2*(ema+1)*ema); + a0 = a3 = 0; + a1 = k*ema; + a2 = -a1; + } break; + case 2 : { + const float + ea = (float)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_instance + "deriche(): Invalid specified filter order %u " + "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + } + coefp = (a0+a1)/(1+b1+b2); + coefn = (a2+a3)/(1+b1+b2); + switch (naxis) { + case 'x' : { + const int N = _width; + const unsigned long off = 1U; + CImg Y(N); + cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; } + } break; + case 'y' : { + const int N = _height; + const unsigned long off = (unsigned long)_width; + CImg Y(N); + cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; } + } break; + case 'z' : { + const int N = _depth; + const unsigned long off = (unsigned long)_width*_height; + CImg Y(N); + cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; } + } break; + default : { + const int N = _spectrum; + const unsigned long off = (unsigned long)_width*_height*_depth; + CImg Y(N); + cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } + } + } + return *this; + } + + //! Apply recursive Deriche filter \newinstance. + CImg get_deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) const { + return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); + } + + // [internal] Apply a recursive filter (used by CImg::vanvliet()). + /** + \param ptr the pointer of the data + \param filter the coefficient of the filter in the following order [n,n-1,n-2,n-3]. + \param N size of the data + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivtive, 2nd derivative, 3rd derivative + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note dirichlet boundary conditions have a strange behavior. And + boundary condition should be corrected using Bill Triggs method (IEEE trans on Sig Proc 2005). + **/ + template + static void _cimg_recursive_apply(T *data, const Tfloat filter[], const int N, const unsigned long off, const int order, const bool boundary_conditions) { + Tfloat val[K]; // res[n,n-1,n-2,n-3,..] or res[n,n+1,n+2,n+3,..] + switch (order) { + case 0 : { + for (int pass = 0; pass<2; ++pass) { + for (int k = 1; k0; --k) val[k] = val[k-1]; + } + if (!pass) data-=off; + } + } break; + case 1 : { + Tfloat x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0); + for (int k = 0; k0; --k) x[k] = x[k-1]; + } else data-=off; + for (int k = K-1; k>0; --k) val[k] = val[k-1]; + } + *data = (T)0; + } + } break; + case 2: { + Tfloat x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0); + for (int k = 0; k0; --k) x[k] = x[k-1]; + for (int k = K-1; k>0; --k) val[k] = val[k-1]; + } + *data = (T)0; + } + } break; + case 3: { + Tfloat x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0); + for (int k = 0; k0; --k) x[k] = x[k-1]; + for (int k = K-1; k>0; --k) val[k] = val[k-1]; + } + *data = (T)0; + } + } break; + } + } + + //! Van Vliet recursive Gaussian filter. + /** + \param sigma standard deviation of the Gaussian filter + \param order the order of the filter 0,1,2,3 + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note dirichlet boundary condition has a strange behavior + + Ian T. Young, Lucas J. van Vliet, Recursive implementation of the + Gaussian filter, Signal Processing, Volume 44, Issue 2, June 1995, + Pages 139-151, + **/ + CImg& vanvliet(const float sigma, const int order, const char axis='x', const bool boundary_conditions=true) { + if (is_empty()) return *this; + const char naxis = cimg::uncase(axis); + const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.1f && !order)) return *this; + const Tfloat + nnsigma = nsigma<0.1f?0.1f:nsigma, + q = (Tfloat)(nnsigma<2.5?3.97156-4.14554*std::sqrt(1-0.2689*nnsigma):0.98711*nnsigma-0.96330), + b0 = 1.57825f + 2.44413f*q + 1.4281f*q*q + 0.4222205f*q*q*q, + b1 = (2.44413f*q + 2.85619f*q*q + 1.26661f*q*q*q), + b2 = -(1.4281f*q*q + 1.26661f*q*q*q), + b3 = 0.4222205f*q*q*q, + B = 1.f - (b1 + b2 + b3)/b0; + Tfloat filter[4]; + filter[0] = B; filter[1] = b1/b0; filter[2] = b2/b0; filter[3] = b3/b0; + + switch (naxis) { + case 'x' : { +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(3) +#endif + cimg_forYZC(*this,y,z,c) _cimg_recursive_apply<4>(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); + } break; + case 'y' : { +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(3) +#endif + cimg_forXZC(*this,x,z,c) _cimg_recursive_apply<4>(data(x,0,z,c),filter,_height,(unsigned long)_width,order,boundary_conditions); + } break; + case 'z' : { +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(3) +#endif + cimg_forXYC(*this,x,y,c) _cimg_recursive_apply<4>(data(x,y,0,c),filter,_depth,(unsigned long)(_width*_height),order,boundary_conditions); + } break; + default : { +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(3) +#endif + cimg_forXYZ(*this,x,y,z) _cimg_recursive_apply<4>(data(x,y,z,0),filter,_spectrum,(unsigned long)(_width*_height*_depth),order,boundary_conditions); + } + } + return *this; + } + + //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. + CImg get_vanvliet(const float sigma, const int order, const char axis='x', const bool boundary_conditions=true) const { + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); + } + + //! Blur image. + /** + \param sigma_x Standard deviation of the blur, along the X-axis. + \param sigma_y Standard deviation of the blur, along the Y-axis. + \param sigma_z Standard deviation of the blur, along the Z-axis. + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. + \note + - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur. + - This is a recursive algorithm, not depending on the values of the standard deviations. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=false) { + if (!is_empty()) { + if (is_gaussian) { + if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); + if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions); + if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions); + } else { + if (_width>1) deriche(sigma_x,0,'x',boundary_conditions); + if (_height>1) deriche(sigma_y,0,'y',boundary_conditions); + if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions); + } + } + return *this; + } + + //! Blur image \newinstance. + CImg get_blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=false) const { + return CImg(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically. + /** + \param sigma Standard deviation of the blur. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) { + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically \newinstance. + CImg get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const { + return CImg(*this,false).blur(sigma,boundary_conditions,is_gaussian); + } + + //! Blur image anisotropically, directed by a field of diffusion tensors. + /** + \param G Field of square roots of diffusion tensors/vectors 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 diffusion process. + \param interpolation_type Interpolation scheme. Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=1) { + + // Check arguments and init variables + if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6)) + throw CImgArgumentException(_cimg_instance + "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", + cimg_instance, + G._width,G._height,G._depth,G._spectrum,G._data); + + if (is_empty() || amplitude<=0 || dl<0) return *this; + const bool is_3d = (G._spectrum==6); + T val_min, val_max = max_min(val_min); + + if (da<=0) { // Iterated oriented Laplacians + CImg velocity(_width,_height,_depth,_spectrum); + for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) { + Tfloat *ptrd = velocity._data, veloc_max = 0; + if (is_3d) { // 3d version + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixx = Incc + Ipcc - 2*Iccc, + ixy = (Innc + Ippc - Inpc - Ipnc)/4, + ixz = (Incn + Ipcp - Incp - Ipcn)/4, + iyy = Icnc + Icpc - 2*Iccc, + iyz = (Icnn + Icpp - Icnp - Icpn)/4, + izz = Iccn + Iccp - 2*Iccc, + veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz + G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } else { // 2d version + CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + if (veloc_max>0) *this+=(velocity*=dl/veloc_max); + } + } else { // LIC-based smoothing. + const unsigned long whd = (unsigned long)_width*_height*_depth; + const float sqrt2amplitude = (float)std::sqrt(2*amplitude); + const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; + CImg res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum); + int N = 0; + if (is_3d) { // 3d version + for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) { + const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp; + for (float theta = 0; theta<360; (theta+=da2),++N) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)*std::cos(phir)), + vy = (float)(std::sin(thetar)*std::cos(phir)), + vz = (float)std::sin(phir); + const t + *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2), + *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3); + 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)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; + } + + Tfloat *ptrd = res._data; + cimg_forXYZ(*this,x,y,z) { + val.fill(0); + const float + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y, + Z = (float)z; + switch (interpolation_type) { + case 0 : { // Nearest neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f), + cz = (int)(Z+0.5f); + const float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const 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 (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + default : { // 2nd order Runge Kutta + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const 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)), + 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 (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + } + Tfloat *_ptrd = ptrd++; + if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; } + else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,z,c)); _ptrd+=whd; } + } + } + } + } else { // 2d LIC algorithm + for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) { + const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); + const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(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)std::sqrt(1e-5+u*u+v*v), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)n; + } + Tfloat *ptrd = res._data; + cimg_forXY(*this,x,y) { + val.fill(0); + const float + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y; + switch (interpolation_type) { + case 0 : { // Nearest-neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f); + const float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u = (float)(W._linear_atXY(X,Y,0,0)), + v = (float)(W._linear_atXY(X,Y,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + default : { // 2nd-order Runge-kutta interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), + v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)), + u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)), + v = (float)(W._linear_atXY(X+u0,Y+v0,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } + } + Tfloat *_ptrd = ptrd++; + if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; } + else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,0,c)); _ptrd+=whd; } + } + } + } + const Tfloat *ptrs = res._data; + cimg_for(*this,ptrd,T) { const Tfloat val = *(ptrs++)/N; *ptrd = valval_max?val_max:(T)val); } + } + return *this; + } + + //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. + template + CImg get_blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way. + /** + \param amplitude Amplitude of the smoothing. + \param sharpness Sharpness. + \param anisotropy Anisotropy. + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) { + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3), + amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way \newinstance. + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image, with the bilateral filter. + /** + \param sigma_x Amount of blur along the X-axis. + \param sigma_y Amount of blur along the Y-axis. + \param sigma_z Amount of blur along the Z-axis. + \param sigma_r Amount of blur along the value axis. + \param bgrid_x Size of the bilateral grid along the X-axis. + \param bgrid_y Size of the bilateral grid along the Y-axis. + \param bgrid_z Size of the bilateral grid along the Z-axis. + \param bgrid_r Size of the bilateral grid along the value axis. + \param interpolation_type Use interpolation for image slicing. + \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 + (extended for 3d volumetric images). + **/ + CImg& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, + const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, + const bool interpolation_type=true) { + if (is_empty()) return *this; + T m, M = max_min(m); + const float range = (float)(1.0f + M - m); + const unsigned int + bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100, + by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100, + bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100, + br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100), + bx = bx0>0?bx0:1, + by = by0>0?by0:1, + bz = bz0>0?bz0:1, + br = br0>0?br0:1; + const float + _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100, + _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100, + _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100, + nsigma_x = _sigma_x*bx/_width, + nsigma_y = _sigma_y*by/_height, + nsigma_z = _sigma_z*bz/_depth, + nsigma_r = sigma_r*br/range; + if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) { + const bool is_3d = (_depth>1); + if (is_3d) { // 3d version of the algorithm + CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); + cimg_forC(*this,c) { + bgrid.fill(0); bgridw.fill(0); + cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + 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(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false); + bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false); + if (interpolation_type) cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + 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_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R); + (*this)(x,y,z,c) = (T)(bval0/bval1); + } else cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + 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,c) = (T)(bval0/bval1); + } + } + } else { // 2d version of the algorithm + CImg bgrid(bx,by,br,2); + cimg_forC(*this,c) { + bgrid.fill(0); + cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + 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(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false); + if (interpolation_type) cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + 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,c) = (T)(bval0/bval1); + } else cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + 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,c) = (T)(bval0/bval1); + } + } + } + } + return *this; + } + + //! Blur image, with the bilateral filter \newinstance. + CImg get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, + const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, + const bool interpolation_type=true) const { + return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type); + } + + //! Blur image using the bilateral filter. + /** + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param bgrid_s Size of the bilateral grid along the XYZ-axes. + \param bgrid_r Size of the bilateral grid along the value axis. + \param interpolation_type Use interpolation for image slicing. + **/ + CImg& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, + const bool interpolation_type=true) { + const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; + return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type); + } + + //! Blur image using the bilateral filter \newinstance. + CImg get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, + const bool interpolation_type=true) const { + return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type); + } + + //! Blur image using patch-based space. + /** + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_p Amount of blur along the value axis. + \param patch_size Size of the patchs. + \param lookup_size Size of the window to search similar patchs. + \param smoothness Smoothness for the patch comparison. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + CImg& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + if (is_empty() || !patch_size || !lookup_size) return *this; + return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); + } + + //! Blur image using patch-based space \newinstance. + CImg get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) const { + +#define _cimg_blur_patch3d_fast(N) \ + cimg_for##N##XYZ(res,x,y,z) { \ + T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \ + 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(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0) - img(p,q,r,0))3?0.0f:1.0f; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ + } \ + if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } + +#define _cimg_blur_patch3d(N) \ + cimg_for##N##XYZ(res,x,y,z) { \ + T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + float sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \ + T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \ + float distance2 = 0; \ + pQ = Q._data; 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)std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \ + if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } + +#define _cimg_blur_patch2d_fast(N) \ + cimg_for##N##XY(res,x,y) { \ + T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + float sum_weights = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))3?0.0f:1.0f; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ + } \ + if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } + +#define _cimg_blur_patch2d(N) \ + cimg_for##N##XY(res,x,y) { \ + T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + float sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \ + T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \ + float distance2 = 0; \ + pQ = Q._data; 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)std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \ + if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } + + if (is_empty() || !patch_size || !lookup_size) return +*this; + CImg res(_width,_height,_depth,_spectrum,0); + const CImg _img = smoothness>0?get_blur(smoothness):CImg(),&img = smoothness>0?_img:*this; + CImg P(patch_size*patch_size*_spectrum), Q(P); + const float + nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100, + sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p, + Pnorm = P.size()*sigma_p2; + const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1; + const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size; + if (_depth>1) switch (patch_size) { // 3d + case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break; + default : { + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) cimg_forXYZ(res,x,y,z) { // Fast + P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,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(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0)-img(p,q,r,0))3?0.0f:1.0f; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); + } + if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } else cimg_forXYZ(res,x,y,z) { // Exact + P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,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, weight_max = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { + (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,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)std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); + if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } + } + } else switch (patch_size) { // 2d + case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; + case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; + case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; + case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; + case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; + case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; + case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break; + default : { // Fast + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation. + P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + float sum_weights = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))3?0.0f:1.0f; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); + } + if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } else cimg_forXY(res,x,y) { // 2d exact algorithm. + P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + float sum_weights = 0, weight_max = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { + (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,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)std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); + if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; + else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c)); + } + } + } + return res; + } + + //! Blur image with the median filter. + /** + \param n Size of the median filter. + **/ + CImg& blur_median(const unsigned int n) { + if (!n) return *this; + return get_blur_median(n).move_to(*this); + } + + //! Blur image with the median filter \newinstance. + CImg get_blur_median(const unsigned int n) const { + if (is_empty() || n<=1) return +*this; + CImg res(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + const int hl = n/2, hr = hl - 1 + n%2; + if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d + 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>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1; + *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); + } else { +#define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) + if (res._height!=1) switch (n) { // 2d + case 3 : { + T I[9] = { 0 }; + CImg_3x3(J,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + 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); + *(ptrd++) = Jcc; + } + } break; + case 5 : { + T I[25] = { 0 }; + CImg_5x5(J,T); + cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) { + 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); + *(ptrd++) = Jcc; + } + } break; + default : { + cimg_forXYC(*this,x,y,c) { + 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>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1; + *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); + } + } + } else switch (n) { // 1d + case 2 : { + T I[4] = { 0 }; + cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1])); + } break; + case 3 : { + T I[9] = { 0 }; + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) + *(ptrd++) = I[3]=width()?width()-1:x1; + *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median(); + } + } + } + } + return res; + } + + //! Sharpen image. + /** + \param amplitude Sharpening amplitude + \param sharpen_type Select sharpening method. Can be { false=inverse diffusion | true=shock filters }. + \param edge Edge threshold (shock filters only). + \param alpha Gradient smoothness (shock filters only). + \param sigma Tensor smoothness (shock filters only). + **/ + CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) { + if (is_empty()) return *this; + T val_min, val_max = max_min(val_min); + const float nedge = edge/2; + CImg val, vec, velocity(_width,_height,_depth,_spectrum); + Tfloat *ptrd = velocity._data, veloc_max = 0; + + if (_depth>1) { // 3d + CImg_3x3x3(I,Tfloat); + if (sharpen_type) { // Shock filters. + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2), *ptrG3 = G.data(0,0,0,3); + cimg_forXYZ(G,x,y,z) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + if (val[2]<0) val[2] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = vec(0,2); + *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge); + } + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + 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 = Incc + Ipcc - 2*Iccc, + ixy = (Innc + Ippc - Inpc - Ipnc)/4, + ixz = (Incn + Ipcp - Incp - Ipcn)/4, + iyy = Icnc + Icpc - 2*Iccc, + iyz = (Icnn + Icpp - Icnp - Icpn)/4, + izz = Iccn + Iccp - 2*Iccc, + ixf = Incc - Iccc, + ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, + iyb = Iccc - Icpc, + izf = Iccn - Iccc, + izb = 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 = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } else cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion. + const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } else { + CImg_3x3(I,Tfloat); + if (sharpen_type) { // Shock filters. + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2); + cimg_forXY(G,x,y) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge); + } + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + ixf = Inc - Icc, + ixb = Icc - Ipc, + iyf = Icn - Icc, + iyb = 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 = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion. + const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + if (veloc_max<=0) return *this; + return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this); + } + + //! Sharpen image \newinstance. + CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const { + return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); + } + + //! Return image gradient. + /** + \param axes Axes considered for the gradient computation, as a C-string (e.g "xy"). + \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. + - 5 = Using Van Vliet recusrsive filter. + **/ + CImgList get_gradient(const char *const axes=0, const int scheme=3) const { + CImgList grad(2,_width,_height,_depth,_spectrum); + Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data; + bool is_3d = 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' : is_3d = true; break; + default : + throw CImgArgumentException(_cimg_instance + "get_gradient(): Invalid specified axis '%c'.", + cimg_instance, + axis); + } + } + } else is_3d = (_depth>1); + if (is_3d) { + CImg(_width,_height,_depth,_spectrum).move_to(grad); + Tfloat *ptrd2 = grad[2]._data; + switch (scheme) { // 3d. + case -1 : { // Backward finite differences. + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = Iccc - Ipcc; + *(ptrd1++) = Iccc - Icpc; + *(ptrd2++) = Iccc - Iccp; + } + } break; + case 1 : { // Forward finite differences. + CImg_2x2x2(I,Tfloat); + cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = Incc - Iccc; + *(ptrd1++) = Icnc - Iccc; + *(ptrd2++) = 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; + case 5 : { // Using Van Vliet filter with low standard variation. + grad[0] = get_vanvliet(0,1,'x'); + grad[1] = get_vanvliet(0,1,'y'); + grad[2] = get_vanvliet(0,1,'z'); + } break; + default : { // Central finite differences. + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = (Incc - Ipcc)/2; + *(ptrd1++) = (Icnc - Icpc)/2; + *(ptrd2++) = (Iccn - Iccp)/2; + } + } + } + } else switch (scheme) { // 2d. + case -1 : { // Backward finite differences. + CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = Icc - Ipc; + *(ptrd1++) = Icc - Icp; + } + } break; + case 1 : { // Forward finite differences. + CImg_2x2(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = Inc - Icc; + *(ptrd1++) = Icn - Icc; + } + } break; + case 2 : { // Sobel scheme. + CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = -Ipp - 2*Ipc - Ipn + Inp + 2*Inc + Inn; + *(ptrd1++) = -Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn; + } + } break; + case 3 : { // Rotation invariant mask. + CImg_3x3(I,Tfloat); + const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1)); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + } break; + case 4 : { // using Van Vliet filter with low standard variation + grad[0] = get_deriche(0,1,'x'); + grad[1] = get_deriche(0,1,'y'); + } break; + case 5 : { // using Deriche filter with low standard variation + grad[0] = get_vanvliet(0,1,'x'); + grad[1] = get_vanvliet(0,1,'y'); + } break; + default : { // central finite differences + CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = (Inc - Ipc)/2; + *(ptrd1++) = (Icn - Icp)/2; + } + } + } + if (!axes) return grad; + CImgList res; + for (unsigned int l = 0; axes[l]; ++l) { + const char axis = cimg::uncase(axes[l]); + switch (axis) { + case 'x' : res.insert(grad[0]); break; + case 'y' : res.insert(grad[1]); break; + case 'z' : res.insert(grad[2]); break; + } + } + grad.assign(); + return res; + } + + //! Return image hessian. + /** + \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). + **/ + CImgList get_hessian(const char *const axes=0) const { + CImgList res; + const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz"; + if (!axes) naxes = _depth>1?def_axes3d:def_axes2d; + const unsigned int lmax = std::strlen(naxes); + if (lmax%2) + throw CImgArgumentException(_cimg_instance + "get_hessian(): Invalid specified axes '%s'.", + cimg_instance, + naxes); + + res.assign(lmax/2,_width,_height,_depth,_spectrum); + if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d + Tfloat + *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data, + *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data; + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + *(ptrd0++) = Ipcc + Incc - 2*Iccc; // Ixx + *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy + *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz + *(ptrd3++) = Icpc + Icnc - 2*Iccc; // Iyy + *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz + *(ptrd5++) = Iccn + Iccp - 2*Iccc; // Izz + } + } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d + Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data; + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx + *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy + *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy + } + } else for (unsigned int l = 0; laxis2) cimg::swap(axis1,axis2); + bool valid_axis = false; + Tfloat *ptrd = res[l2]._data; + if (axis1=='x' && axis2=='x') { // Ixx + valid_axis = true; CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc; + } + else if (axis1=='x' && axis2=='y') { // Ixy + valid_axis = true; CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4; + } + else if (axis1=='x' && axis2=='z') { // Ixz + valid_axis = true; CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4; + } + else if (axis1=='y' && axis2=='y') { // Iyy + valid_axis = true; CImg_3x3(I,Tfloat); + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc; + } + else if (axis1=='y' && axis2=='z') { // Iyz + valid_axis = true; CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4; + } + else if (axis1=='z' && axis2=='z') { // Izz + valid_axis = true; CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc; + } + else if (!valid_axis) + throw CImgArgumentException(_cimg_instance + "get_hessian(): Invalid specified axes '%s'.", + cimg_instance, + naxes); + } + return res; + } + + //! Compute image laplacian. + CImg& laplacian() { + return get_laplacian().move_to(*this); + } + + //! Compute image laplacian \newinstance. + CImg get_laplacian() const { + if (is_empty()) return CImg(); + CImg res(_width,_height,_depth,_spectrum); + Tfloat *ptrd = res._data; + if (_depth>1) { // 3d + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) + *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; + } else if (_height>1) { // 2d + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) + *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; + } else { // 1d + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) + *(ptrd++) = Inc + Ipc - 2*Icc; + } + return res; + } + + //! Compute the structure tensor field of an image. + /** + \param scheme Numerical scheme. Can be { 0=central | 1=fwd/bwd1 | 2=fwd/bwd2 } + **/ + CImg& structure_tensors(const unsigned int scheme=2) { + return get_structure_tensors(scheme).move_to(*this); + } + + //! Compute the structure tensor field of an image \newinstance. + CImg get_structure_tensors(const unsigned int scheme=2) const { + if (is_empty()) return *this; + CImg res; + if (_depth>1) { // 3d + res.assign(_width,_height,_depth,6,0); + CImg_3x3x3(I,Tfloat); + switch (scheme) { + case 0 : { // classical central finite differences + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ix = (Incc - Ipcc)/2, + iy = (Icnc - Icpc)/2, + iz = (Iccn - Iccp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=ix*iz; + *(ptrd3++)+=iy*iy; + *(ptrd4++)+=iy*iz; + *(ptrd5++)+=iz*iz; + } + } + } break; + case 1 : { // Forward/backward finite differences (version 1). + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixf = Incc - Iccc, ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, + izf = Iccn - Iccc, izb = Iccc - Iccp; + *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*ixf + ixb*ixb)/4; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; + *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4; + *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; + *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4; + } + } + } break; + default : { // Forward/backward finite differences (version 2). + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixf = Incc - Iccc, ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, + izf = Iccn - Iccc, izb = Iccc - Iccp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; + *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; + *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; + *(ptrd5++)+=(izf*izf + izb*izb)/2; + } + } + } break; + } + } else { // 2d + res.assign(_width,_height,_depth,3,0); + CImg_3x3(I,Tfloat); + switch (scheme) { + case 0 : { // classical central finite differences + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ix = (Inc - Ipc)/2, + iy = (Icn - Icp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=iy*iy; + } + } + } break; + case 1 : { // Forward/backward finite differences (version 1). + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ixf = Inc - Icc, ixb = Icc - Ipc, + iyf = Icn - Icc, iyb = Icc - Icp; + *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*iyf + ixb*ixb)/4; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4; + } + } + } break; + default : { // Forward/backward finite differences (version 2). + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ixf = Inc - Icc, ixb = Icc - Ipc, + iyf = Icn - Icc, iyb = Icc - Icp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; + } + } + } break; + } + } + return res; + } + + //! Compute field of diffusion tensors for edge-preserving smoothing. + /** + \param sharpness Sharpness + \param anisotropy Anisotropy + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param is_sqrt Tells if the square root of the tensor field is computed instead. + **/ + CImg& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) { + CImg res; + const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy); + blur(alpha).normalize(0,(T)255); + + if (_depth>1) { // 3d + CImg val(3), vec(3,3); + get_structure_tensors().move_to(res).blur(sigma); + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + cimg_forXYZ(*this,x,y,z) { + res.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float + _l1 = val[2], _l2 = val[1], _l3 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3: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)std::pow(1+l1+l2+l3,-power1), + n2 = (float)std::pow(1+l1+l2+l3,-power2); + *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx; + *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy; + *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz; + *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy; + *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz; + *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } + } else { // for 2d images + CImg val(2), vec(2,2); + get_structure_tensors().move_to(res).blur(sigma); + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + cimg_forXY(*this,x,y) { + res.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float + _l1 = val[1], _l2 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)std::pow(1+l1+l2,-power1), + n2 = (float)std::pow(1+l1+l2,-power2); + *(ptrd0++) = n1*ux*ux + n2*vx*vx; + *(ptrd1++) = n1*ux*uy + n2*vx*vy; + *(ptrd2++) = n1*uy*uy + n2*vy*vy; + } + } + return res.move_to(*this); + } + + //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. + CImg get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const { + return CImg(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); + } + + //! Estimate displacement field between two images. + /** + \param source Reference image. + \param smoothness Smoothness of estimated displacement field. + \param precision Precision required for algorithm convergence. + \param nb_scales Number of scales used to estimate the displacement field. + \param iteration_max Maximum number of iterations allowed for one scale. + \param is_backward If false, match I2(X+U(X)) = I1(X), else match I2(X) = I1(X-U(X)). + **/ + CImg& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.0f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this); + } + + //! Estimate displacement field between two images \newinstance. + CImg get_displacement(const CImg& source, + const float smoothness=0.1f, const float precision=5.0f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false) const { + if (is_empty() || !source) return +*this; + if (!is_sameXYZC(source)) + throw CImgArgumentException(_cimg_instance + "displacement(): Instance and source image (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + source._width,source._height,source._depth,source._spectrum,source._data); + if (precision<0) + throw CImgArgumentException(_cimg_instance + "displacement(): Invalid specified precision %g " + "(should be >=0)", + cimg_instance, + precision); + const unsigned int _nb_scales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height)))); + const float _precision = (float)std::pow(10.0,-(double)precision); + float sm, sM = source.max_min(sm), tm, tM = max_min(tm); + const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); + const bool is_3d = source._depth>1; + CImg U; + + for (int scale = _nb_scales-1; scale>=0; --scale) { + const float factor = (float)std::pow(1.5,(double)scale); + const unsigned int + _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1, + _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1, + _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1; + if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // skip too small scales. + const CImg + I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, + I2 = (get_resize(I1,2)-=tm)/=tdelta; + if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); + else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); + float dt = 2, energy = cimg::type::max(); + const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); + + for (unsigned int iteration = 0; iteration=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization. + const float + X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0), + Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1), + Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2); + float delta_I = 0, _energy_regul = 0; + if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); + else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c)); + cimg_forC(U,c) { + const float + Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)), + Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)), + Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)), + Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c), + Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c), + Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c); + U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) + smoothness* ( Uxx + Uyy + Uzz)))/(1+6*smoothness*dt); + _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } else { + const float nsmoothness = -smoothness; + cimg_for3XYZ(U,x,y,z) { // Anisotropic regularization. + const float + X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0), + Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1), + Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2); + float delta_I = 0, _energy_regul = 0; + if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); + else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c)); + cimg_forC(U,c) { + const float + Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)), + Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)), + Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)), + N2 = Ux*Ux + Uy*Uy + Uz*Uz, + N = std::sqrt(N2), + N3 = 1e-5 + N2*N, + coef_a = (1 - Ux*Ux/N2)/N, + coef_b = -2*Ux*Uy/N3, + coef_c = -2*Ux*Uz/N3, + coef_d = (1 - Uy*Uy/N2)/N, + coef_e = -2*Uy*Uz/N3, + coef_f = (1 - Uz*Uz/N2)/N, + Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c), + Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c), + Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c), + Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)), + Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)), + Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c)); + U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) + + nsmoothness* ( coef_a*Uxx + coef_b*Uxy + coef_c*Uxz + coef_d*Uyy + coef_e*Uyz + coef_f*Uzz )) + )/(1+2*(coef_a+coef_d+coef_f)*nsmoothness*dt); + _energy_regul+=N; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + } + } else { // 2d version. + if (smoothness>=0) cimg_for3XY(U,x,y) { // Isotropic regularization. + const float + X = is_backward?x - U(x,y,0):x + U(x,y,0), + Y = is_backward?y - U(x,y,1):y + U(x,y,1); + float delta_I = 0, _energy_regul = 0; + if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c)); + else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c)); + cimg_forC(U,c) { + const float + Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)), + Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)), + Uxx = U(_n1x,y,c) + U(_p1x,y,c), + Uyy = U(x,_n1y,c) + U(x,_p1y,c); + U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + smoothness*( Uxx + Uyy )))/(1+4*smoothness*dt); + _energy_regul+=Ux*Ux + Uy*Uy; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } else { + const float nsmoothness = -smoothness; + cimg_for3XY(U,x,y) { // Anisotropic regularization. + const float + X = is_backward?x - U(x,y,0):x + U(x,y,0), + Y = is_backward?y - U(x,y,1):y + U(x,y,1); + float delta_I = 0, _energy_regul = 0; + if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c)); + else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c)); + cimg_forC(U,c) { + const float + Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)), + Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)), + N2 = Ux*Ux + Uy*Uy, + N = std::sqrt(N2), + N3 = 1e-5 + N2*N, + coef_a = Uy*Uy/N3, + coef_b = -2*Ux*Uy/N3, + coef_c = Ux*Ux/N3, + Uxx = U(_n1x,y,c) + U(_p1x,y,c), + Uyy = U(x,_n1y,c) + U(x,_p1y,c), + Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c)); + U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/(1+2*(coef_a+coef_c)*nsmoothness*dt); + _energy_regul+=N; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + } + } + const float d_energy = (_energy - energy)/(sw*sh*sd); + if (d_energy<=0 && -d_energy<_precision) break; + if (d_energy>0) dt*=0.5f; + energy = _energy; + } + } + return U; + } + + //! Compute distance to a specified value. + /** + \param value Reference value. + \param metric Type of metric. Can be { 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }. + \note + The distance transform implementation has been submitted by A. Meijster, and implements + the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink, + "A general algorithm for computing distance transforms in linear time.", + In: Mathematical Morphology and its Applications to Image and Signal Processing, + J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.' + The submitted code has then been modified to fit CImg coding style and constraints. + **/ + CImg& distance(const T value, const unsigned int metric=2) { + if (is_empty()) return *this; + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; + if (!is_value) return fill(cimg::type::max()); + switch (metric) { + case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev. + case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan. + case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean. + default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean. + } + return *this; + } + + //! Compute distance to a specified value \newinstance. + CImg get_distance(const T value, const unsigned int metric=2) const { + return CImg(*this,false).distance((Tfloat)value,metric); + } + + static long _distance_sep_edt(const long i, const long u, const long *const g) { + return (u*u-i*i+g[u]-g[i])/(2*(u-i)); + } + + static long _distance_dist_edt(const long x, const long i, const long *const g) { + return (x-i)*(x-i) + g[i]; + } + + static long _distance_sep_mdt(const long i, const long u, const long *const g) { + return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2); + } + + static long _distance_dist_mdt(const long x, const long i, const long *const g) { + return (x=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } + if (q<0) { q = 0; s[0] = u; } + else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }} + } + for (int u = (int)len-1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan. + } + + CImg& _distance_core(long (*const sep)(const long, const long, const long *const), + long (*const f)(const long, const long, const long *const)) { + const unsigned long wh = (unsigned long)_width*_height; + cimg_forC(*this,c) { + CImg g(_width), dt(_width), s(_width), t(_width); + CImg img = get_shared_channel(c); + cimg_forYZ(*this,y,z) { // Over X-direction. + cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh); + _distance_scan(_width,g,sep,f,s,t,dt); + cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x]; + } + g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); + cimg_forXZ(*this,x,z) { // Over Y-direction. + cimg_forY(*this,y) g[y] = (long)img(x,y,z,0,wh); + _distance_scan(_height,g,sep,f,s,t,dt); + cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y]; + } + if (_depth>1) { + g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth); + cimg_forXY(*this,x,y) { // Over Z-direction. + cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh); + _distance_scan(_depth,g,sep,f,s,t,dt); + cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z]; + } + } + } + return *this; + } + + //! Compute chamfer distance to a specified value, with a custom metric. + /** + \param value Reference value. + \param metric_mask Metric mask. + \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. + **/ + template + CImg& distance(const T value, const CImg& metric_mask) { + if (is_empty()) return *this; + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; + if (!is_value) return fill(cimg::type::max()); + const unsigned long wh = (unsigned long)_width*_height; + cimg_forC(*this,c) { + CImg img = get_shared_channel(c); + cimg_forXYZ(metric_mask,dx,dy,dz) { + const t weight = metric_mask(dx,dy,dz); + if (weight) { + for (int z = dz, nz = 0; z=0; --z,--nz) { // Backward scan. + for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) { + for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) { + const T dd = img(nx,ny,nz,0,wh) + weight; + if (dd + CImg get_distance(const T value, const CImg& metric_mask) const { + return CImg(*this,false).distance(value,metric_mask); + } + + //! Compute distance map to one source point. + /** + \param x X-coordinate of the source point. + \param y Y-coordinate of the source point. + \param z Z-coordinate of the source point. + \note At input, image instance represents a field of potentials. + **/ + CImg& distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return get_distance_dijkstra(x,y,z).move_to(*this); + } + + //! Compute distance map to one source point \newinstance. + CImg get_distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + if (is_empty()) return *this; + if (!containsXYZC(x,y,z,0)) + throw CImgArgumentException(_cimg_instance + "distance_dijkstra(): image instance does not contain specified starting point (%u,%u,%u).", + cimg_instance, + x,y,z); + if (_spectrum!=1) + throw CImgInstanceException(_cimg_instance + "distance_dijkstra(): image instance is not a scalar image.", + cimg_instance); + CImg res(_width,_height,_depth,2); + CImg in_queue(_width,_height,_depth,1,0); + CImg Q; + unsigned int sizeQ = 0; + + // Put specified point in priority queue. + Q._priority_queue_insert(in_queue,sizeQ,0,x,y,z); + res(x,y,z) = 0; res(x,y,z,1) = 0; + + // Start distance propagation. + while (sizeQ) { + + // Get and remove point with minimal potential from the queue. + const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); + const Tfloat potential = (Tfloat)-Q(0,0); + Q._priority_queue_remove(sizeQ); + + // Update neighbors. + Tfloat npot = 0; + if (x-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x-1,y,z)+potential),x-1,y,z)) { + res(x-1,y,z) = npot; res(x-1,y,z,1) = 2; + } + if (x+1=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y-1,z)+potential),x,y-1,z)) { + res(x,y-1,z) = npot; res(x,y-1,z,1) = 4; + } + if (y+1=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z-1)+potential),x,y,z-1)) { + res(x,y,z-1) = npot; res(x,y,z-1,1) = 6; + } + if (z+1& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { + if (is_empty()) return *this; + CImg velocity(*this); + for (unsigned int iteration = 0; iteration1) { // 3d + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)0?(Incc - Iccc):(Iccc - Ipcc), + iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc), + iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp), + ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)), + ngx = gx/ng, + ngy = gy/ng, + ngz = gz/ng, + veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } else { // 2d version + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)0?(Inc - Icc):(Icc - Ipc), + iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), + ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)), + ngx = gx/ng, + ngy = gy/ng, + veloc = sgn*(ngx*ix + ngy*iy - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } + if (veloc_max>0) *this+=(velocity*=time_step/veloc_max); + } + return *this; + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. + CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const { + return CImg(*this,false).distance_eikonal(nb_iterations,band_size,time_step); + } + + //! Compute Haar multiscale wavelet transform. + /** + \param axis Axis considered for the transform. + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(axis,invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { + if (is_empty() || !nb_scales) return +*this; + CImg res; + const Tfloat sqrt2 = std::sqrt(2); + if (nb_scales==1) { + switch (cimg::uncase(axis)) { // Single scale transform + case 'x' : { + const unsigned int w = _width/2; + if (w) { + if ((w%2) && w!=1) + throw CImgInstanceException(_cimg_instance + "haar(): Sub-image width %u is not even.", + cimg_instance, + w); + + res.assign(_width,_height,_depth,_spectrum); + if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X + for (unsigned int x = 0, xw = w, x2 = 0; x& haar(const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { + CImg res; + if (nb_scales==1) { // Single scale transform + if (_width>1) get_haar('x',invert,1).move_to(res); + if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); } + if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); } + if (res) return res; + } else { // Multi-scale transform + if (invert) { // Inverse transform + res.assign(*this); + if (_width>1) { + if (_height>1) { + if (_depth>1) { + unsigned int w = _width, h = _height, d = _depth; for (unsigned int s = 1; w && h && d && s1) { + unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s1) { + if (_depth>1) { + unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s1) { + unsigned int d = _depth; for (unsigned int s = 1; d && s1) { + if (_height>1) { + if (_depth>1) for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s1) { + if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) for (unsigned int s = 1, d = _depth/2; d && s get_FFT(const char axis, const bool is_invert=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],axis,is_invert); + return res; + } + + //! Compute n-d Fast Fourier Transform. + /* + \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + CImgList get_FFT(const bool is_invert=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],is_invert); + return res; + } + + //! Compute 1d Fast Fourier Transform, along a specified axis. + /** + \param[in,out] real Real part of the pixel values. + \param[in,out] imag Imaginary part of the pixel values. + \param axis Axis along which the FFT is computed. + \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + static void FFT(CImg& real, CImg& imag, const char axis, const bool is_invert=false) { + if (!real) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", + pixel_type()); + + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); +#ifdef cimg_use_fftw3 + fftw_complex *data_in; + fftw_plan data_plan; + + switch (cimg::uncase(axis)) { + case 'x' : { // Fourier along X, using FFTW library. + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width); + if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the X-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width), + real._width,real._height,real._depth,real._spectrum); + + data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forYZC(real,y,z,c) { + T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c); + double *ptrd = (double*)data_in; + cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } + fftw_execute(data_plan); + const unsigned int fact = real._width; + if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); } + else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); } + } + } break; + case 'y' : { // Fourier along Y, using FFTW library. + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height); + if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Y-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._height), + real._width,real._height,real._depth,real._spectrum); + + data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned int off = real._width; + cimg_forXZC(real,x,z,c) { + T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c); + double *ptrd = (double*)data_in; + cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = real._height; + if (is_invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } + else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } break; + case 'z' : { // Fourier along Z, using FFTW library. + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth); + if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Z-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._depth), + real._width,real._height,real._depth,real._spectrum); + + data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned long off = (unsigned long)real._width*real._height; + cimg_forXYC(real,x,y,c) { + T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c); + double *ptrd = (double*)data_in; + cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = real._depth; + if (is_invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } + else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } break; + default : { // Fourier along C, using FFTW library. + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum); + if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the C-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._spectrum), + real._width,real._height,real._depth,real._spectrum); + + data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned long off = (unsigned long)real._width*real._height*real._depth; + cimg_forXYZ(real,x,y,z) { + T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0); + double *ptrd = (double*)data_in; + cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = real._spectrum; + if (is_invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } + else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } + } + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#else + switch (cimg::uncase(axis)) { + case 'x' : { // Fourier along X, using built-in functions. + const unsigned int N = real._width, N2 = (N>>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the X-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forYZC(real,y,z,c) { + cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Y-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXZC(real,x,z,c) { + cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Z-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXYC(real,x,y,c) { + cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i::FFT(): Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) " + "(should be { x | y | z }).", + pixel_type(),axis, + real._width,real._height,real._depth,real._spectrum); + } +#endif + } + + //! Compute n-d Fast Fourier Transform. + /** + \param[in,out] real Real part of the pixel values. + \param[in,out] imag Imaginary part of the pixel values. + \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + static void FFT(CImg& real, CImg& imag, const bool is_invert=false) { + if (!real) + throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", + pixel_type()); + + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + +#ifdef cimg_use_fftw3 + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u).", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth*real._spectrum), + real._width,real._height,real._depth,real._spectrum); + + fftw_plan data_plan; + const unsigned long w = (unsigned long)real._width, wh = w*real._height, whd = wh*real._depth; + data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c); + double *ptrd = (double*)data_in; + for (unsigned int x = 0; x1) FFT(real,imag,'z',is_invert); + if (real._height>1) FFT(real,imag,'y',is_invert); + if (real._width>1) FFT(real,imag,'x',is_invert); +#endif + } + + //@} + //------------------------------------- + // + //! \name 3d Objects Management + //@{ + //------------------------------------- + + //! Shift 3d object's vertices. + /** + \param tx X-coordinate of the 3d displacement vector. + \param ty Y-coordinate of the 3d displacement vector. + \param tz Z-coordinate of the 3d displacement vector. + **/ + CImg& shift_object3d(const float tx, const float ty=0, const float tz=0) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3d vertices.", + cimg_instance); + + get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; + return *this; + } + + //! Shift 3d object's vertices \newinstance. + CImg get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { + return CImg(*this,false).shift_object3d(tx,ty,tz); + } + + //! Shift 3d object's vertices, so that it becomes centered. + /** + \note The object center is computed as its barycenter. + **/ + CImg& shift_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3d vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm); + xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; + return *this; + } + + //! Shift 3d object's vertices, so that it becomes centered \newinstance. + CImg get_shift_object3d() const { + return CImg(*this,false).shift_object3d(); + } + + //! Resize 3d object. + /** + \param sx Width of the 3d object's bounding box. + \param sy Height of the 3d object's bounding box. + \param sz Depth of the 3d object's bounding box. + **/ + CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3d vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm); + if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } + if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } + if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } + return *this; + } + + //! Resize 3d object \newinstance. + CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { + return CImg(*this,false).resize_object3d(sx,sy,sz); + } + + //! Resize 3d object to unit size. + CImg resize_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3d vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(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; + } + + //! Resize 3d object to unit size \newinstance. + CImg get_resize_object3d() const { + return CImg(*this,false).resize_object3d(); + } + + //! Merge two 3d objects together. + /** + \param[in,out] primitives Primitives data of the current 3d object. + \param obj_vertices Vertices data of the additional 3d object. + \param obj_primitives Primitives data of the additional 3d object. + **/ + template + CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, const CImgList& obj_primitives) { + if (!obj_vertices || !obj_primitives) return *this; + if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a set of 3d vertices.", + cimg_instance, + obj_vertices._width,obj_vertices._height,obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data); + + if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); } + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Instance is not a set of 3d vertices.", + cimg_instance); + + const unsigned int P = _width; + append(obj_vertices,'x'); + const unsigned int N = primitives._width; + primitives.insert(obj_primitives); + for (unsigned int i = N; i &p = primitives[i]; + switch (p.size()) { + case 1 : p[0]+=P; break; // Point. + case 5 : p[0]+=P; p[1]+=P; break; // Sphere. + case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment. + case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle. + case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle. + } + } + return *this; + } + + //! Texturize primitives of a 3d object. + /** + \param[in,out] primitives Primitives data of the 3d object. + \param[in,out] colors Colors data of the 3d object. + \param texture Texture image to map to 3d object. + \param coords Texture-mapping coordinates. + **/ + template + const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, + const CImg& texture, const CImg& coords=CImg::empty()) const { + if (is_empty()) return *this; + if (_height!=3) + throw CImgInstanceException(_cimg_instance + "texturize_object3d(): image instance is not a set of 3d points.", + cimg_instance); + if (coords && (coords._width!=_width || coords._height!=2)) + throw CImgArgumentException(_cimg_instance + "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", + cimg_instance, + coords._width,coords._height,coords._depth,coords._spectrum,coords._data); + CImg _coords; + if (!coords) { // If no texture coordinates specified, do a default XY-projection. + _coords.assign(_width,2); + float + xmin, xmax = (float)get_shared_row(0).max_min(xmin), + ymin, ymax = (float)get_shared_row(1).max_min(ymin), + dx = xmax>xmin?xmax-xmin:1, + dy = ymax>ymin?ymax-ymin:1; + cimg_forX(*this,p) { + _coords(p,0) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx); + _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy); + } + } else _coords = coords; + + int texture_ind = -1; + cimglist_for(primitives,l) { + CImg &p = primitives[l]; + const unsigned int siz = p.size(); + switch (siz) { + case 1 : { // Point. + const unsigned int + i0 = (unsigned int)p[0], + x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1); + texture.get_vector_at(x0,y0).move_to(colors[l]); + } break; + case 2 : case 6 : { // Line. + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], + x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), + x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1); + if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,x0,y0,x1,y1).move_to(p); + } break; + case 3 : case 9 : { // Triangle. + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], + x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), + x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1), + x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1); + if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p); + } break; + case 4 : case 12 : { // Quadrangle. + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3], + x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), + x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1), + x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1), + x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1); + if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); + } break; + } + } + return *this; + } + + //! Generate a 3d elevation of the image instance. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param[out] colors The returned list of the 3d object colors. + \param elevation The input elevation map. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + CImgList colors3d; + const CImg points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); + CImg().display_object3d("Elevation3d",points3d,faces3d,colors3d); + \endcode + \image html ref_elevation3d.jpg + **/ + template + CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { + if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1) + throw CImgArgumentException(_cimg_instance + "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + elevation._width,elevation._height,elevation._depth,elevation._spectrum,elevation._data); + if (is_empty()) return *this; + float m, M = (float)max_min(m); + if (M==m) ++M; + colors.assign(); + const unsigned int size_x1 = _width - 1, size_y1 = _height - 1; + for (unsigned int y = 0; y1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r, + b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r); + CImg::vector((tc)r,(tc)g,(tc)b).move_to(colors); + } + const typename CImg::_functor2d_int func(elevation); + return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height); + } + + //! Generate the 3d projection planes of the image instance. + /** + \param[out] primitives Primitives data of the returned 3d object. + \param[out] colors Colors data of the returned 3d object. + \param x0 X-coordinate of the projection point. + \param y0 Y-coordinate of the projection point. + \param z0 Z-coordinate of the projection point. + \param normalize_colors Tells if the created textures have normalized colors. + **/ + template + CImg get_projections3d(CImgList& primitives, CImgList& colors, + const unsigned int x0, const unsigned int y0, const unsigned int z0, + const bool normalize_colors=false) const { + float m = 0, M = 0, delta = 1; + if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); } + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + CImg img_xy, img_xz, img_yz; + if (normalize_colors) { + ((get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1)-=m)*=delta).move_to(img_xy); + ((get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1)-=m)*=delta).resize(_width,_depth,1,-100,-1).move_to(img_xz); + ((get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1)-=m)*=delta).resize(_height,_depth,1,-100,-1).move_to(img_yz); + } else { + get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1).move_to(img_xy); + get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1).move_to(img_xz); + get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).resize(_height,_depth,1,-100,-1).move_to(img_yz); + } + CImg points(12,3,1,1, + 0,_width-1,_width-1,0, 0,_width-1,_width-1,0, _x0,_x0,_x0,_x0, + 0,0,_height-1,_height-1, _y0,_y0,_y0,_y0, 0,_height-1,_height-1,0, + _z0,_z0,_z0,_z0, 0,0,_depth-1,_depth-1, 0,0,_depth-1,_depth-1); + primitives.assign(); + CImg::vector(0,1,2,3,0,0,img_xy._width-1,0,img_xy._width-1,img_xy._height-1,0,img_xy._height-1).move_to(primitives); + CImg::vector(4,5,6,7,0,0,img_xz._width-1,0,img_xz._width-1,img_xz._height-1,0,img_xz._height-1).move_to(primitives); + CImg::vector(8,9,10,11,0,0,img_yz._width-1,0,img_yz._width-1,img_yz._height-1,0,img_yz._height-1).move_to(primitives); + colors.assign(); + img_xy.move_to(colors); + img_xz.move_to(colors); + img_yz.move_to(colors); + return points; + } + + //! Generate a isoline of the image instance as a 3d object. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3d object colors. + \param size_x The number of subdivisions along the X-axis. + \param size_y The number of subdisivions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + const CImg points3d = img.get_isoline3d(faces3d,100); + CImg().display_object3d("Isoline3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isoline3d.jpg + **/ + template + CImg get_isoline3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a scalar image.", + cimg_instance); + if (_depth>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a 2d image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) { + const _functor2d_int func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,width(),height()); + } else { + const _functor2d_float func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y); + } + return vertices; + } + + //! Generate an isosurface of the image instance as a 3d object. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3d object colors. + \param size_x Number of subdivisions along the X-axis. + \param size_y Number of subdisivions along the Y-axis. + \param size_z Number of subdisivions along the Z-axis. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + const CImg img = CImg("reference.jpg").resize(-100,-100,20); + CImgList faces3d; + const CImg points3d = img.get_isosurface3d(faces3d,100); + CImg().display_object3d("Isosurface3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isosurface3d.jpg + **/ + template + CImg get_isosurface3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100, const int size_z=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isosurface3d(): Instance is not a scalar image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) { + const _functor3d_int func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,width(),height(),depth()); + } else { + const _functor3d_float func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z); + } + return vertices; + } + + //! Compute 3d elevation of a function as a 3d object. + /** + \param[out] primitives Primitives data of the resulting 3d object. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + **/ + template + static CImg elevation3d(CImgList& primitives, const tfunc& func, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const float + nx0 = x0=0?size_x:(nx1-nx0)*-size_x/100), nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1, + _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1; + if (nsize_x<2 || nsize_y<2) + throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", + pixel_type(), + nsize_x,nsize_y); + + CImg vertices(nsize_x*nsize_y,3); + floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2); + for (unsigned int y = 0; y + static CImg elevation3d(CImgList& primitives, const char *const expression, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); + } + + //! Compute 0-isolines of a function, as a 3d object. + /** + \param[out] primitives Primitives data of the resulting 3d object. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static CImg isoline3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static const 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)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nxm1 = nx - 1, + nym1 = ny - 1; + primitives.assign(); + if (!nxm1 || !nym1) return CImg(); + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1; + CImgList vertices; + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = x0, Y = y0, nX = X + dx, nY = Y + dy; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; } + + // Run the marching squares algorithm + for (unsigned int yi = 0, nyi = 1; yi::vector(Xi,Y,0).move_to(vertices); + } + if ((edge&2) && indices1(nxi,1)<0) { + const float Yi = Y + (isovalue-val1)*dy/(val2-val1); + indices1(nxi,1) = vertices._width; + CImg::vector(nX,Yi,0).move_to(vertices); + } + if ((edge&4) && indices2(xi,0)<0) { + const float Xi = X + (isovalue-val3)*dx/(val2-val3); + indices2(xi,0) = vertices._width; + CImg::vector(Xi,nY,0).move_to(vertices); + } + if ((edge&8) && indices1(xi,1)<0) { + const float Yi = Y + (isovalue-val0)*dy/(val3-val0); + indices1(xi,1) = vertices._width; + CImg::vector(X,Yi,0).move_to(vertices); + } + + // Create segments + for (const int *segment = segments[configuration]; *segment!=-1; ) { + const unsigned int p0 = *(segment++), p1 = *(segment++); + const tf + i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)), + i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi)); + CImg::vector(i0,i1).move_to(primitives); + } + } + } + values1.swap(values2); + indices1.swap(indices2); + } + return vertices>'x'; + } + + //! Compute isolines of a function, as a 3d object \overloading. + template + static CImg isoline3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); + } + + template + static int _isoline3d_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0 : return (int)indices1(x,0); + case 1 : return (int)indices1(nx,1); + case 2 : return (int)indices2(x,0); + case 3 : return (int)indices1(x,1); + } + return 0; + } + + //! Compute isosurface of a function, as a 3d object. + /** + \param[out] primitives Primitives data of the resulting 3d object. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static CImg isosurface3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x=32, const int size_y=32, const int size_z=32) { + 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)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nz = _nz?_nz:1, + nxm1 = nx - 1, + nym1 = ny - 1, + nzm1 = nz - 1; + primitives.assign(); + if (!nxm1 || !nym1 || !nzm1) return CImg(); + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1; + CImgList vertices; + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; + + // Fill the first plane with function values + Y = y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; } + Y+=dy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + dz; + for (unsigned int zi = 0; zi::vector(Xi,Y,Z).move_to(vertices); + } + if ((edge&2) && indices1(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val1)*dy/(val2-val1); + indices1(nxi,yi,1) = vertices._width; + CImg::vector(nX,Yi,Z).move_to(vertices); + } + if ((edge&4) && indices1(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val3)*dx/(val2-val3); + indices1(xi,nyi,0) = vertices._width; + CImg::vector(Xi,nY,Z).move_to(vertices); + } + if ((edge&8) && indices1(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val0)*dy/(val3-val0); + indices1(xi,yi,1) = vertices._width; + CImg::vector(X,Yi,Z).move_to(vertices); + } + if ((edge&16) && indices2(xi,yi,0)<0) { + const float Xi = X + (isovalue-val4)*dx/(val5-val4); + indices2(xi,yi,0) = vertices._width; + CImg::vector(Xi,Y,nZ).move_to(vertices); + } + if ((edge&32) && indices2(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val5)*dy/(val6-val5); + indices2(nxi,yi,1) = vertices._width; + CImg::vector(nX,Yi,nZ).move_to(vertices); + } + if ((edge&64) && indices2(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val7)*dx/(val6-val7); + indices2(xi,nyi,0) = vertices._width; + CImg::vector(Xi,nY,nZ).move_to(vertices); + } + if ((edge&128) && indices2(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val4)*dy/(val7-val4); + indices2(xi,yi,1) = vertices._width; + CImg::vector(X,Yi,nZ).move_to(vertices); + } + if ((edge&256) && indices1(xi,yi,2)<0) { + const float Zi = Z+ (isovalue-val0)*dz/(val4-val0); + indices1(xi,yi,2) = vertices._width; + CImg::vector(X,Y,Zi).move_to(vertices); + } + if ((edge&512) && indices1(nxi,yi,2)<0) { + const float Zi = Z + (isovalue-val1)*dz/(val5-val1); + indices1(nxi,yi,2) = vertices._width; + CImg::vector(nX,Y,Zi).move_to(vertices); + } + if ((edge&1024) && indices1(nxi,nyi,2)<0) { + const float Zi = Z + (isovalue-val2)*dz/(val6-val2); + indices1(nxi,nyi,2) = vertices._width; + CImg::vector(nX,nY,Zi).move_to(vertices); + } + if ((edge&2048) && indices1(xi,nyi,2)<0) { + const float Zi = Z + (isovalue-val3)*dz/(val7-val3); + indices1(xi,nyi,2) = vertices._width; + CImg::vector(X,nY,Zi).move_to(vertices); + } + + // Create triangles + for (const int *triangle = triangles[configuration]; *triangle!=-1; ) { + const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); + const tf + i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), + i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), + i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); + CImg::vector(i0,i2,i1).move_to(primitives); + } + } + } + } + cimg::swap(values1,values2); + cimg::swap(indices1,indices2); + } + return vertices>'x'; + } + + //! Compute isosurface of a function, as a 3d object \overloading. + template + static CImg isosurface3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int dx=32, const int dy=32, const int dz=32) { + const _functor3d_expr func(expression); + return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz); + } + + template + static int _isosurface3d_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0 : return indices1(x,y,0); + case 1 : return indices1(nx,y,1); + case 2 : return indices1(x,ny,0); + case 3 : return indices1(x,y,1); + case 4 : return indices2(x,y,0); + case 5 : return indices2(nx,y,1); + case 6 : return indices2(x,ny,0); + case 7 : return indices2(x,y,1); + case 8 : return indices1(x,y,2); + case 9 : return indices1(nx,y,2); + case 10 : return indices1(nx,ny,2); + case 11 : return indices1(x,ny,2); + } + return 0; + } + + // Define functors for accessing image values (used in previous functions). + struct _functor2d_int { + const CImg& ref; + _functor2d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _functor2d_float { + const CImg& ref; + _functor2d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref._linear_atXY(x,y); + } + }; + + struct _functor2d_expr { + _cimg_math_parser *mp; + _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } + ~_functor2d_expr() { delete mp; } + float operator()(const float x, const float y) const { + return (float)mp->eval(x,y,0,0); + } + }; + + struct _functor3d_int { + const CImg& ref; + _functor3d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _functor3d_float { + const CImg& ref; + _functor3d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref._linear_atXYZ(x,y,z); + } + }; + + struct _functor3d_expr { + _cimg_math_parser *mp; + ~_functor3d_expr() { delete mp; } + _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } + float operator()(const float x, const float y, const float z) const { + return (float)mp->eval(x,y,z,0); + } + }; + + struct _functor4d_int { + const CImg& ref; + _functor4d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref((int)x,(int)y,(int)z,c); + } + }; + + //! Generate a 3d box object. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the box (dimension along the X-axis). + \param size_y The height of the box (dimension along the Y-axis). + \param size_z The depth of the box (dimension along the Z-axis). + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::box3d(faces3d,10,20,30); + CImg().display_object3d("Box3d",points3d,faces3d); + \endcode + \image html ref_box3d.jpg + **/ + template + static CImg box3d(CImgList& primitives, + const float size_x=200, const float size_y=100, const float size_z=100) { + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + 0.,size_x,size_x, 0., 0.,size_x,size_x, 0., + 0., 0.,size_y,size_y, 0., 0.,size_y,size_y, + 0., 0., 0., 0.,size_z,size_z,size_z,size_z); + } + + //! Generate a 3d cone. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cone basis. + \param size_z The cone's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cone3d(faces3d,50); + CImg().display_object3d("Cone3d",points3d,faces3d); + \endcode + \image html ref_cone3d.jpg + **/ + template + static CImg cone3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,size_z, + 0.,0.,0.); + for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); + } + const unsigned int nbr = vertices._width - 2; + for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); + CImg::vector(0,curr,next).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3d cylinder. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cylinder basis. + \param size_z The cylinder's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cylinder3d(faces3d,50); + CImg().display_object3d("Cylinder3d",points3d,faces3d); + \endcode + \image html ref_cylinder3d.jpg + **/ + template + static CImg cylinder3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,0., + 0.,0.,size_z); + for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); + } + const unsigned int nbr = (vertices._width - 2)/2; + for (unsigned int p = 0; p::vector(0,next,curr).move_to(primitives); + CImg::vector(1,curr+1,next+1).move_to(primitives); + CImg::vector(curr,next,next+1,curr+1).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3d torus. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius1 The large radius. + \param radius2 The small radius. + \param subdivisions1 The number of angular subdivisions for the large radius. + \param subdivisions2 The number of angular subdivisions for the small radius. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::torus3d(faces3d,20,4); + CImg().display_object3d("Torus3d",points3d,faces3d); + \endcode + \image html ref_torus3d.jpg + **/ + template + static CImg torus3d(CImgList& primitives, + const float radius1=100, const float radius2=30, + const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { + primitives.assign(); + if (!subdivisions1 || !subdivisions2) return CImg(); + CImgList vertices; + for (unsigned int v = 0; v::vector(x,y,z).move_to(vertices); + } + } + for (unsigned int vv = 0; vv::vector(svv+nu,svv+uu,snv+uu,snv+nu).move_to(primitives); + } + } + return vertices>'x'; + } + + //! Generate a 3d XY-plane. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the plane (dimension along the X-axis). + \param size_y The height of the plane (dimensions along the Y-axis). + \param subdivisions_x The number of planar subdivisions along the X-axis. + \param subdivisions_y The number of planar subdivisions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::plane3d(faces3d,100,50); + CImg().display_object3d("Plane3d",points3d,faces3d); + \endcode + \image html ref_plane3d.jpg + **/ + template + static CImg plane3d(CImgList& primitives, + const float size_x=100, const float size_y=100, + const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) { + primitives.assign(); + if (!subdivisions_x || !subdivisions_y) return CImg(); + CImgList vertices; + const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1; + const float fx = (float)size_x/w, fy = (float)size_y/h; + for (unsigned int y = 0; y::vector(fx*x,fy*y,0).move_to(vertices); + for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3d sphere. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the sphere (dimension along the X-axis). + \param subdivisions The number of recursive subdivisions from an initial icosahedron. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::sphere3d(faces3d,100,4); + CImg().display_object3d("Sphere3d",points3d,faces3d); + \endcode + \image html ref_sphere3d.jpg + **/ + template + static CImg sphere3d(CImgList& primitives, + const float radius=50, const unsigned int subdivisions=3) { + + // Create initial icosahedron + primitives.assign(); + const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a; + CImgList vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b, + -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); + // edge - length/2 + float he = (float)a; + + // Recurse subdivisions + for (unsigned int i = 0; i::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; } + if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; } + if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; } + primitives.remove(0); + CImg::vector(p0,i0,i1).move_to(primitives); + CImg::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); + CImg::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives); + CImg::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives); + } + } + return (vertices>'x')*=radius; + } + + //! Generate a 3d ellipsoid. + /** + \param[out] primitives The returned list of the 3d object primitives + (template type \e tf should be at least \e unsigned \e int). + \param tensor The tensor which gives the shape and size of the ellipsoid. + \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron. + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N-1). + \par Example + \code + CImgList faces3d; + const CImg tensor = CImg::diagonal(10,7,3), + points3d = CImg::ellipsoid3d(faces3d,tensor,4); + CImg().display_object3d("Ellipsoid3d",points3d,faces3d); + \endcode + \image html ref_ellipsoid3d.jpg + **/ + template + static CImg ellipsoid3d(CImgList& primitives, + const CImg& tensor, const unsigned int subdivisions=3) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImg S, V; + tensor.symmetric_eigen(S,V); + const float orient = + (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) + + (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) + + (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2); + if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); } + const float l0 = S[0], l1 = S[1], l2 = S[2]; + CImg vertices = sphere3d(primitives,1.0,subdivisions); + vertices.get_shared_row(0)*=l0; + vertices.get_shared_row(1)*=l1; + vertices.get_shared_row(2)*=l2; + return V*vertices; + } + + //! Convert 3d object into a CImg3d representation. + /** + \param primitives Primitives data of the 3d object. + \param colors Colors data of the 3d object. + \param opacities Opacities data of the 3d object. + **/ + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities) { + return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this); + } + + //! Convert 3d object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors) { + return get_object3dtoCImg3d(primitives,colors).move_to(*this); + } + + //! Convert 3d object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives) { + return get_object3dtoCImg3d(primitives).move_to(*this); + } + + //! Convert 3d object into a CImg3d representation \overloading. + CImg& object3dtoCImg3d() { + return get_object3dtoCImg3d().move_to(*this); + } + + //! Convert 3d object into a CImg3d representation \newinstance. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities) const { + char error_message[1024] = { 0 }; + if (!is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgInstanceException(_cimg_instance + "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).", + cimg_instance,_width,primitives._width,error_message); + CImg res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); + float *ptrd = res._data; + + // Put magick number. + *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f; + *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f; + + // Put number of vertices and primitives. + *(ptrd++) = cimg::uint2float(_width); + *(ptrd++) = cimg::uint2float(primitives._width); + + // Put vertex data. + if (is_empty() || !primitives) return res; + const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2); + cimg_forX(*this,p) { + *(ptrd++) = (float)*(ptrx++); + *(ptrd++) = (float)*(ptry++); + *(ptrd++) = (float)*(ptrz++); + } + + // Put primitive data. + cimglist_for(primitives,p) { + *(ptrd++) = (float)primitives[p].size(); + const tp *ptrp = primitives[p]._data; + cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++)); + } + + // Put color/texture data. + const unsigned int csiz = cimg::min(colors._width,primitives._width); + for (int c = 0; c<(int)csiz; ++c) { + const CImg& color = colors[c]; + const tc *ptrc = color._data; + if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; } + else { + *(ptrd++) = -128.0f; + int shared_ind = -1; + if (color.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImgList& opacities, float *ptrd) const { + cimglist_for(opacities,o) { + const CImg& opacity = opacities[o]; + const to *ptro = opacity._data; + if (opacity.size()==1) *(ptrd++) = (float)*ptro; + else { + *(ptrd++) = -128.0f; + int shared_ind = -1; + if (opacity.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImg& opacities, float *ptrd) const { + const to *ptro = opacities._data; + cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++); + return ptrd; + } + + template + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImgList& opacities) const { + unsigned int siz = 8 + 3*width(); + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) { + if (colors[c].is_shared()) siz+=4; + else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; } + } + if (colors._width + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImg& opacities) const { + unsigned int siz = 8 + 3*width(); + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) { + const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; + } + if (colors._width + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors) const { + CImgList opacities; + return get_object3dtoCImg3d(primitives,colors,opacities); + } + + //! Convert 3d object into a CImg3d representation \overloading. + template + CImg get_object3dtoCImg3d(const CImgList& primitives) const { + CImgList colors, opacities; + return get_object3dtoCImg3d(primitives,colors,opacities); + } + + //! Convert 3d object into a CImg3d representation \overloading. + CImg get_object3dtoCImg3d() const { + CImgList opacities, colors; + CImgList primitives(width(),1,1,1,1); + cimglist_for(primitives,p) primitives(p,0) = p; + return get_object3dtoCImg3d(primitives,colors,opacities); + } + + //! Convert CImg3d representation into a 3d object. + /** + \param[out] primitives Primitives data of the 3d object. + \param[out] colors Colors data of the 3d object. + \param[out] opacities Opacities data of the 3d object. + **/ + template + CImg& CImg3dtoobject3d(CImgList& primitives, CImgList& colors, CImgList& opacities) { + return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this); + } + + //! Convert CImg3d representation into a 3d object \newinstance. + template + CImg get_CImg3dtoobject3d(CImgList& primitives, CImgList& colors, CImgList& opacities) const { + char error_message[1024] = { 0 }; + if (!is_CImg3d(true,error_message)) + throw CImgInstanceException(_cimg_instance + "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", + cimg_instance,error_message); + const T *ptrs = _data + 6; + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + const CImg points = CImg(ptrs,3,nb_points,1,1,true).get_transpose(); + ptrs+=3*nb_points; + primitives.assign(nb_primitives); + cimglist_for(primitives,p) { + const unsigned int nb_inds = (unsigned int)*(ptrs++); + primitives[p].assign(1,nb_inds); + tp *ptrp = primitives[p]._data; + for (unsigned int i = 0; i + CImg& _draw_scanline(const int x0, const int x1, const int y, + const tc *const color, const float opacity=1, + const float brightness=1, const bool init=false) { + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + static float nopacity = 0, copacity = 0; + static unsigned long whd = 0; + static const tc *col = 0; + if (init) { + nopacity = cimg::abs(opacity); + copacity = 1 - cimg::max(opacity,0); + whd = (unsigned long)_width*_height*_depth; + } else { + const int nx0 = x0>0?x0:0, nx1 = x1=0) { + col = color; + const unsigned long off = whd - dx - 1; + T *ptrd = data(nx0,y); + if (opacity>=1) { // ** Opaque drawing ** + if (brightness==1) { // Brightness==1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)*(col++); + std::memset(ptrd,(int)val,dx+1); + ptrd+=whd; + } + } else if (brightness<1) { // Brightness<1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + std::memset(ptrd,(int)val,dx+1); + ptrd+=whd; + } + } else { // Brightness>1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + std::memset(ptrd,(int)val,dx+1); + ptrd+=whd; + } + } + } else { // ** Transparent drawing ** + if (brightness==1) { // Brightness==1 + cimg_forC(*this,c) { + 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_forC(*this,c) { + 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_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } + } + } + } + return *this; + } + + template + CImg& _draw_scanline(const tc *const color, const float opacity=1) { + return _draw_scanline(0,0,0,color,opacity,0,true); + } + + //! Draw a 3d point. + /** + \param x0 X-coordinate of the point. + \param y0 Y-coordinate of the point. + \param z0 Z-coordinate of the point. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_point(50,50,color); + \endcode + **/ + template + CImg& draw_point(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_point(): Specified color is (null).", + cimg_instance); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } + } + return *this; + } + + //! Draw a 2d point \simplification. + template + CImg& draw_point(const int x0, const int y0, + const tc *const color, const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + // Draw a points cloud. + /** + \param points Image of vertices coordinates. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_point(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity); + } break; + default : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity); + } + } + return *this; + } + + //! Draw a 2d 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 spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + \note + - Line routine uses Bresenham's algorithm. + - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,color); + \endcode + **/ + template + CImg& draw_line(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=width()) return *this; + if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; } + if (xright>=width()) { yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft)); xright = width() - 1; } + if (ydown<0 || yup>=height()) return *this; + if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; } + if (ydown>=height()) { xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup)); ydown = height() - 1; } + T *ptrd0 = data(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 long + offx = (nx0=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + T *ptrd = ptrd0; const tc* col = color; + cimg_forC(*this,c) { *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_forC(*this,c) { *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_forC(*this,c) { *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_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a 2d line, with z-buffering. + /** + \param zbuffer Zbuffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + **/ + template + CImg& draw_line(CImg& 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) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=width()) return *this; + if (xleft<0) { + const float D = (float)xright - xleft; + yleft-=(int)((float)xleft*((float)yright - yleft)/D); + zleft-=(tzfloat)xleft*(zright - zleft)/D; + xleft = 0; + } + if (xright>=width()) { + const float d = (float)xright - width(), D = (float)xright - xleft; + yright-=(int)(d*((float)yright - yleft)/D); + zright-=(tzfloat)d*(zright - zleft)/D; + xright = width() - 1; + } + if (ydown<0 || yup>=height()) return *this; + if (yup<0) { + const float D = (float)ydown - yup; + xup-=(int)((float)yup*((float)xdown - xup)/D); + zup-=(tzfloat)yup*(zdown - zup)/D; + yup = 0; + } + if (ydown>=height()) { + const float d = (float)ydown - height(), D = (float)ydown - yup; + xdown-=(int)(d*((float)xdown - xup)/D); + zdown-=(tzfloat)d*(zdown - zup)/D; + ydown = height() - 1; + } + T *ptrd0 = data(nx0,ny0); + tz *ptrz = zbuffer.data(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 long + offx = (nx00?dx:1; + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + const tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz && pattern&hatch) { + *ptrz = (tz)z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forC(*this,c) { *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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forC(*this,c) { *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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz && pattern&hatch) { + *ptrz = (tz)z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forC(*this,c) { *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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forC(*this,c) { *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 3d line. + /** + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + **/ + template + CImg& draw_line(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + 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>=width()) return *this; + if (nx0<0) { const float D = 1.0f + nx1 - nx0; ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D); nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D); nx0 = 0; } + if (nx1>=width()) { const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0; ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); nx1 = width() - 1; } + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (ny1<0 || ny0>=height()) return *this; + if (ny0<0) { const float D = 1.0f + ny1 - ny0; nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D); nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D); ny0 = 0; } + if (ny1>=height()) { const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); ny1 = height() - 1; } + if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (nz1<0 || nz0>=depth()) return *this; + if (nz0<0) { const float D = 1.0f + nz1 - nz0; nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D); ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D); nz0 = 0; } + if (nz1>=depth()) { const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1 = depth() - 1; } + const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0); + const unsigned long whd = (unsigned long)_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 = data((unsigned int)x,(unsigned int)y,(unsigned int)z); + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + } + 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 = data((unsigned int)x,(unsigned int)y,(unsigned int)z); + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } + } + x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } + } + } + return *this; + } + + //! Draw a textured 2d 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. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + \note + - Line routine uses the well known Bresenham's algorithm. + \par Example: + \code + CImg img(100,100,1,3,0), texture("texture256x256.ppm"); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,texture,0,0,255,255); + \endcode + **/ + template + CImg& draw_line(const int x0, const int y0, + const int x1, const int y1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,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=width()) return *this; + if (xleft<0) { + const float D = (float)xright - xleft; + yleft-=(int)((float)xleft*((float)yright - yleft)/D); + txleft-=(int)((float)xleft*((float)txright - txleft)/D); + tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D); + xleft = 0; + } + if (xright>=width()) { + const float d = (float)xright - width(), D = (float)xright - xleft; + yright-=(int)(d*((float)yright - yleft)/D); + txright-=(int)(d*((float)txright - txleft)/D); + tyright-=(int)(d*((float)tyright - tyleft)/D); + xright = width() - 1; + } + if (ydown<0 || yup>=height()) return *this; + if (yup<0) { + const float D = (float)ydown - yup; + xup-=(int)((float)yup*((float)xdown - xup)/D); + txup-=(int)((float)yup*((float)txdown - txup)/D); + tyup-=(int)((float)yup*((float)tydown - tyup)/D); + yup = 0; + } + if (ydown>=height()) { + const float d = (float)ydown - height(), D = (float)ydown - yup; + xdown-=(int)(d*((float)xdown - xup)/D); + txdown-=(int)(d*((float)txdown - txup)/D); + tydown-=(int)(d*((float)tydown - tyup)/D); + ydown = height() - 1; + } + T *ptrd0 = data(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 long + offx = (nx00?dx:1; + const unsigned long wh = (unsigned long)_width*_height; + + 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_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); 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_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); 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_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *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_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a textured 2d line, with perspective correction. + /** + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending 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. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() && z0<=0 && z1<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,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=width()) return *this; + if (xleft<0) { + const float D = (float)xright - xleft; + yleft-=(int)((float)xleft*((float)yright - yleft)/D); + zleft-=(float)xleft*(zright - zleft)/D; + txleft-=(float)xleft*(txright - txleft)/D; + tyleft-=(float)xleft*(tyright - tyleft)/D; + xleft = 0; + } + if (xright>=width()) { + const float d = (float)xright - width(), D = (float)xright - xleft; + yright-=(int)(d*((float)yright - yleft)/D); + zright-=d*(zright - zleft)/D; + txright-=d*(txright - txleft)/D; + tyright-=d*(tyright - tyleft)/D; + xright = width() - 1; + } + if (ydown<0 || yup>=height()) return *this; + if (yup<0) { + const float D = (float)ydown - yup; + xup-=(int)((float)yup*((float)xdown - xup)/D); + zup-=(float)yup*(zdown - zup)/D; + txup-=(float)yup*(txdown - txup)/D; + tyup-=(float)yup*(tydown - tyup)/D; + yup = 0; + } + if (ydown>=height()) { + const float d = (float)ydown - height(), D = (float)ydown - yup; + xdown-=(int)(d*((float)xdown - xup)/D); + zdown-=d*(zdown - zup)/D; + txdown-=d*(txdown - txup)/D; + tydown-=d*(tydown - tyup)/D; + ydown = height() - 1; + } + T *ptrd0 = data(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 long + offx = (nx00?dx:1; + const unsigned long wh = (unsigned long)_width*_height; + + 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_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); 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_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); 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_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *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_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a textured 2d line, with perspective correction and z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending 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. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(CImg& zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,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=width()) return *this; + if (xleft<0) { + const float D = (float)xright - xleft; + yleft-=(int)((float)xleft*((float)yright - yleft)/D); + zleft-=(float)xleft*(zright - zleft)/D; + txleft-=(float)xleft*(txright - txleft)/D; + tyleft-=(float)xleft*(tyright - tyleft)/D; + xleft = 0; + } + if (xright>=width()) { + const float d = (float)xright - width(), D = (float)xright - xleft; + yright-=(int)(d*((float)yright - yleft)/D); + zright-=d*(zright - zleft)/D; + txright-=d*(txright - txleft)/D; + tyright-=d*(tyright - tyleft)/D; + xright = width()-1; + } + if (ydown<0 || yup>=height()) return *this; + if (yup<0) { + const float D = (float)ydown - yup; + xup-=(int)((float)yup*((float)xdown - xup)/D); + zup-=yup*(zdown - zup)/D; + txup-=yup*(txdown - txup)/D; + tyup-=yup*(tydown - tyup)/D; + yup = 0; + } + if (ydown>=height()) { + const float d = (float)ydown - height(), D = (float)ydown - yup; + xdown-=(int)(d*((float)xdown - xup)/D); + zdown-=d*(zdown - zup)/D; + txdown-=d*(txdown - txup)/D; + tydown-=d*(tydown - tyup)/D; + ydown = height()-1; + } + T *ptrd0 = data(nx0,ny0); + tz *ptrz = zbuffer.data(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 long + offx = (nx00?dx:1; + const unsigned long wh = (unsigned long)_width*_height; + + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + const tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); 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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); 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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *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 tzfloat z = Z0 + x*dz/ndx; + if (z>=(tzfloat)*ptrz) { + *ptrz = (tz)z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + } + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a set of consecutive lines. + /** + \param points Coordinates of vertices, stored as a list of vectors. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \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. + **/ + template + CImg& draw_line(const CImg& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + case 2 : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i + CImg& draw_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::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f, + l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; + if (sq>0) { + const float + cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg), + cr = (float)std::cos(ang + deg), sr = (float)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 2d spline. + /** + \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 spectrum() consecutive values of type \c T, defining the drawing color. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + \note + - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points + and corresponding velocity vectors. + - The spline is drawn as a serie of connected segments. The \p precision parameter sets the + average number of pixels in each drawn segment. + - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) } + where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two + \e control points. + The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as + \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,255,255 }; + img.draw_spline(30,30,0,100,90,40,0,-100,color); + \endcode + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const tc *const color, const float opacity=1, + const float precision=0.25, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Specified color is (null).", + cimg_instance); + if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1)); + int ox = x0, oy = y0; + for (float t = 0; t<1; t+=_precision) { + 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 draw_line(ox,oy,x1,y1,color,opacity,pattern,false); + } + + //! Draw a 3d spline \overloading. + /** + \note + - Similar to CImg::draw_spline() for a 3d spline in a volumetric image. + **/ + template + CImg& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, + const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, + const tc *const color, const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Specified color is (null).", + cimg_instance); + if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + az = w0 + w1 + 2*(z0 - z1), + bz = 3*(z1 - z0) - 2*w0 - w1, + _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1)); + int ox = x0, oy = y0, oz = z0; + for (float t = 0; t<1; t+=_precision) { + 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 draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false); + } + + //! Draw a textured 2d spline. + /** + \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. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch if \c true, reinit hatch motif. + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& texture, + const int tx0, const int ty0, const int tx1, const int ty1, + const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_empty()) return *this; + 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); + if (x0==x1 && y0==y1) return draw_point(x0,y0,texture.get_vector_at(x0,y0),opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1)); + int ox = x0, oy = y0, otx = tx0, oty = ty0; + for (float t1 = 0; t1<1; t1+=_precision) { + 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), + nty = ty0 + (int)((ty1-ty0)*t1); + 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 draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); + } + + //! Draw a set of consecutive splines. + /** + \param points Vertices data. + \param tangents Tangents data. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param is_closed_set Tells if the drawn spline set is closed. + \param precision Precision of the drawing. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + **/ + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + case 2 : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); + int ox = x0, oy = y0; + float ou = u0, ov = v0; + for (unsigned int i = 1; i + CImg& draw_spline(const CImg& points, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + CImg tangents; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + tangents.assign(points._width,points._height); + cimg_forX(points,p) { + const unsigned int + p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0), + p1 = is_closed_set?(p+1)%points._width:(p+1=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)) + + // [internal] Draw a filled triangle. + template + CImg& _draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const float brightness) { + _draw_scanline(color,opacity); + const float nbrightness = brightness<0?0:(brightness>2?2:brightness); + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); + if (ny0=0) { + if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0) + _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness); + else + _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness); + } + return *this; + } + + //! Draw a filled 2d triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); + return *this; + } + + //! Draw a outlined 2d triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + 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 filled 2d triangle, with z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param z0 Z-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param z1 Z-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param z2 Z-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param brightness Brightness factor. + **/ + template + CImg& draw_triangle(CImg& 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) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const long whd = (long)_width*_height*_depth, offx = _spectrum*whd; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || ny2<0) return *this; + tzfloat + 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; + tzfloat zleft = zl, zright = zr; + if (xright=width()-1) xright = width() - 1; + T* ptrd = data(xleft,y,0,0); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + ptrd-=offx; + } + zleft+=pentez; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; } + ptrd-=offx; + } + zleft+=pentez; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; } + ptrd-=offx; + } + zleft+=pentez; + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; } + ptrd-=offx; + } + zleft+=pentez; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; } + ptrd-=offx; + } + zleft+=pentez; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; + cimg_forC(*this,c) { + const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; + } + ptrd-=offx; + } + zleft+=pentez; + } + } + zr+=pzr; zl+=pzl; + } + return *this; + } + + //! Draw a Gouraud-shaded 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param brightness0 Brightness factor of the first vertex (in [0,2]). + \param brightness1 brightness factor of the second vertex (in [0,2]). + \param brightness2 brightness factor of the third vertex (in [0,2]). + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), + nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), + nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f); + 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>=height() || ny2<0) return *this; + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc-(dx?dx*(dc/dx):0); + int errc = dx>>1; + if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx; + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width() - 1; + T* ptrd = data(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = color; + cimg_forC(*this,c) { + *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + ptrd+=whd; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } else for (int x = xleft; x<=xright; ++x) { + const tc *col = color; + cimg_forC(*this,c) { + const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2d triangle, with z-buffering \overloading. + template + CImg& draw_triangle(CImg& 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) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, offx = _spectrum*whd; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), + nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), + nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f); + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || ny2<0) return *this; + tzfloat + 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; + tzfloat zleft = zl, zright = zr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright-cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc-(dx?dx*(dc/dx):0); + const tzfloat 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>=width()-1) xright = width()-1; + T *ptrd = data(xleft,y); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; + cimg_forC(*this,c) { + *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + ptrd+=whd; + } + 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>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; + cimg_forC(*this,c) { + const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; + } + ptrd-=offx; + } + zleft+=pentez; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + zr+=pzr; zl+=pzl; + } + return *this; + } + + //! Draw a color-interpolated 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex. + \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the seconf vertex. + \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc1 *const color1, + const tc2 *const color2, + const tc3 *const color3, + const float opacity=1) { + const unsigned char one = 1; + cimg_forC(*this,c) get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); + return *this; + } + + //! Draw a textured 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param opacity Drawing opacity. + \param brightness Brightness factor of the drawing (in [0,2]). + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-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>=height() || ny2<0) return *this; + _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y, + nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrighttxleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errtx = dx>>1, errty = errtx; + if (xleft<0 && dx) { + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)*col; + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)(nbrightness**col); + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } + } + } + return *this; + } + + //! Draw a 2d textured triangle, with perspective correction. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-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>=height() || 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=width()-1) xright = width()-1; + T* ptrd = data(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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)*col; + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nbrightness**col); + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a textured 2d triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd; + 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; + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || 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), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + 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))); + tzfloat + 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; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int xleft = xleft0, xright = xright0; + float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; + tzfloat zleft = zl, zright = zr; + if (xright=width()-1) xright = width()-1; + T *ptrd = data(xleft,y,0,0); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)*col; + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nbrightness**col); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a Phong-shaded 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-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>=height() || ny2<0) return *this; + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = color; + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); + ptrd+=whd; + } + 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 tc *col = color; + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; + } + ptrd-=offx; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } + } + return *this; + } + + //! Draw a Phong-shaded 2d triangle, with z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const tc *const color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, offx = _spectrum*whd; + 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; + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || ny2<0) return *this; + tzfloat + 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; + tzfloat zleft = zl, zright = zr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + const tzfloat 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>=width()-1) xright = width()-1; + T *ptrd = data(xleft,y,0,0); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + const tc cval = *(col++); + *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); + ptrd+=whd; + } + 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>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tc *col = color; + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + 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+=whd; + } + 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 textured Gouraud-shaded 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param brightness0 Brightness factor of the first vertex. + \param brightness1 Brightness factor of the second vertex. + \param brightness2 Brightness factor of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), + nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), + nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f); + 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>=height() || ny2<0) return *this; + _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y, + nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + cleft = cleft0, cright = cright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrightcleft?cright - cleft:cleft - cright, + dtx = txright>txleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rc = dx?(cright - cleft)/dx:0, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + sc = cright>cleft?1:-1, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0), + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errc = dx>>1, errtx = errc, errty = errc; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.data(txleft,tyleft); + cimg_forC(*this,c) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whd; col+=twhd; + } + 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.data(txleft,tyleft); + cimg_forC(*this,c) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 textured Gouraud-shaded 2d triangle, with perspective correction \overloading. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), + nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), + nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f); + 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>=height() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + cleft = cleft0, cright = cright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errc = dx>>1; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + zleft-=xleft*(zright - zleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whd; col+=twhd; + } + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), + nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), + nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f); + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2; + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || 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), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + 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))); + tzfloat + 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; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; + float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; + tzfloat zleft = zl, zright = zr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0); + float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; + const tzfloat pentez = (zright - zleft)/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>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whd; col+=twhd; + } + 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>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 textured Phong-shaded 2d triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-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>=height() || ny2<0) return *this; + _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y, + nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + dtx = txright>txleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0), + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx; + if (xleft<0 && dx) { + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.data(txleft,tyleft); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whd; col+=twhd; + } + 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 tc *col = texture.data(txleft,tyleft); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 textured Phong-shaded 2d triangle, with perspective correction. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-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>=height() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + zleft-=xleft*(zright - zleft)/dx; + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whd; col+=twhd; + } + 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 tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 textured Phong-shaded 2d triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd; + 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; + tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)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>=height() || 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), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + 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))); + tzfloat + 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; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; + tzfloat zleft = zl, zright = zr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; + const tzfloat 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; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=width()-1) xright = width()-1; + T* ptrd = data(xleft,y); + tz *ptrz = zbuffer.data(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whd; col+=twhd; + } + 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>=(tzfloat)*ptrz) { + *ptrz = (tz)zleft; + const tzfloat invz = 1/zleft; + const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forC(*this,c) { + const tl l = light(lxleft,lyleft,c); + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whd; col+=twhd; + } + 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 filled 4d rectangle. + /** + \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 c0 C-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 c1 C-coordinate of the lower-right rectangle corner. + \param val Scalar value used to fill the rectangle area. + \param opacity Drawing opacity. + **/ + CImg& draw_rectangle(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const T val, const float opacity=1) { + if (is_empty()) return *this; + const bool bx = (x0=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0), + lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0), + lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0), + lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0); + const unsigned long + offX = (unsigned long)_width - lX, + offY = (unsigned long)_width*(_height - lY), + offZ = (unsigned long)_width*_height*(_depth - lZ); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0); + if (lX>0 && lY>0 && lZ>0 && lC>0) + for (int v = 0; v=1) { + if (sizeof(T)!=1) { for (int x = 0; x + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_rectangle(): Specified color is (null).", + cimg_instance); + cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); + return *this; + } + + //! Draw an outlined 3d rectangle \overloading. + template + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity, + const unsigned int pattern) { + return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). + draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). + draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). + draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). + draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). + draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). + draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). + draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). + draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); + } + + //! Draw a filled 2d rectangle. + /** + \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 spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1) { + return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity); + } + + //! Draw a outlined 2d rectangle \overloading. + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); + if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); + const bool bx = (x0 + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + + // Normalize 2d input coordinates (remove adjacent duplicates). + CImg npoints(points._width,2); + unsigned int nb_points = 0; + int cx = (int)points(points._width-1,0), cy = (int)points(points._width-1,1); + cimg_forX(points,p) { + const int nx = (int)points(p,0), ny = (int)points(p,1); + if (nx!=cx || ny!=cy) { npoints(nb_points,0) = cx; npoints(nb_points++,1) = cy; cx = nx; cy = ny; } + } + + if (nb_points==1) return draw_point((int)npoints(0,0),(int)npoints(0,1),color,opacity); + if (nb_points==2) return draw_line((int)npoints(0,0),(int)npoints(0,1), + (int)npoints(1,0),(int)npoints(1,1),color,opacity); + if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1), + (int)npoints(1,0),(int)npoints(1,1), + (int)npoints(2,0),(int)npoints(2,1),color,opacity); + if (opacity!=1) { // For non-opaque polygons, do a little trick to avoid horizontal lines artefacts. + npoints.resize(nb_points,2,1,1,0); + CImg npoints_x = npoints.get_shared_row(0), npoints_y = npoints.get_shared_row(1); + int xmax = 0, xmin = (int)npoints_x.min_max(xmax), ymax = 0, ymin = (int)npoints_y.min_max(ymax); + if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; + if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity); + const unsigned int + nxmin = xmin<0?0:(unsigned int)xmin, nxmax = xmax>=width()?_width-1:(unsigned int)xmax, + nymin = ymin<0?0:(unsigned int)ymin, nymax = ymax>=height()?_height-1:(unsigned int)ymax, + dx = 1 + nxmax - nxmin, + dy = 1 + nymax - nymin; + npoints_x-=nxmin; npoints_y-=nymin; + unsigned char one = 1; + const CImg mask = CImg(dx,dy,1,1,0).draw_polygon(npoints,&one,1); + CImg _color(dx,dy,1,spectrum()); + cimg_forC(_color,c) _color.get_shared_channel(c).fill(color[c]); + return draw_image(nxmin,nymin,0,0,_color,mask,opacity,1); + } + + // Draw polygon segments. + _draw_scanline(color,1); + int + xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax), + ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax); + if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; + if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,1); + const unsigned int + nymin = ymin<0?0:(unsigned int)ymin, + nymax = ymax>=height()?_height-1:(unsigned int)ymax, + dy = 1 + nymax - nymin; + CImg X(1+2*nb_points,dy,1,1,0), tmp; + cx = (int)npoints(0,0), cy = (int)npoints(0,1); + unsigned int cp = 0; + for (unsigned int p = 0; pay && cy>ny))?1:0; + for (int x = cx, y = y0, _sx = 1, _sy = 1, + _dx = nx>cx?nx-cx:((_sx=-1),cx-nx), + _dy = y1>y0?y1-y0:((_sy=-1),y0-y1), + _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy), + _err = _dx>>1, + _rx = _dy?(nx-cx)/_dy:0; + _counter>=countermin; + --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0)) + if (y>=0 && y<(int)dy) X(++X(0,y),y) = x; + cp = np; cx = nx; cy = ny; + } else { + const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1); + if (y0>=0 && y0<(int)dy) { + _draw_scanline(cxpy && ay>cy) || (cy + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity, const unsigned int pattern) { + if (is_empty() || !points || points._width<3) return *this; + bool ninit_hatch = true; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set.", + cimg_instance); + case 2 : { // 2d version. + CImg npoints(points._width,2); + int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); + unsigned int nb_points = 1; + for (unsigned int p = 1; p npoints(points._width,3); + int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1), z = npoints(0,2) = (int)points(0,2); + unsigned int nb_points = 1; + for (unsigned int p = 1; p + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity=1) { + return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U); + } + + //! Draw a filled 2d ellipse \overloading. + /** + \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 Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity); + } + + //! Draw an outlined 2d 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 angle Angle of the first radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity, const unsigned int pattern) { + if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern); + return *this; + } + + //! Draw an outlined 2d ellipse \overloading. + /** + \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 Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity, + const unsigned int pattern) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity,pattern); + } + + template + CImg& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_ellipse(): Specified color is (null).", + cimg_instance); + if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity); + _draw_scanline(color,opacity); + const float + nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), + nangle = (float)(angle*cimg::PI/180), + u = (float)std::cos(nangle), + v = (float)std::sin(nangle), + rmax = cimg::max(nr1,nr2), + l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2), + l2 = (float)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)std::sqrt(a*rmax*rmax/(a*c - b*b)), + tymin = y0 - yb - 1, + tymax = y0 + yb + 1, + ymin = tymin<0?0:tymin, + ymax = tymax>=height()?height()-1:tymax; + int oxmin = 0, oxmax = 0; + bool first_line = true; + for (int y = ymin; y<=ymax; ++y) { + const float + Y = y - y0 + (y0?(float)std::sqrt(delta)/a:0.0f, + bY = b*Y/a, + fxmin = x0 - 0.5f - bY - sdelta, + fxmax = x0 + 0.5f - bY + sdelta; + const int xmin = (int)fxmin, xmax = (int)fxmax; + if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity); + else { + if (first_line) { + if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity); + else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); + first_line = false; + } else { + if (xmin + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + _draw_scanline(color,opacity); + if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this; + if (y0>=0 && y0=0) { + const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity, + const unsigned int pattern) { + cimg::unused(pattern); + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this; + if (!radius) return draw_point(x0,y0,color,opacity); + draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity). + draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity); + if (radius==1) return *this; + for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x=0) { f+=(ddFy+=2); --y; } + ++x; ++(f+=(ddFx+=2)); + if (x!=y+1) { + const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y; + draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). + draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); + if (x!=y) + draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). + draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); + } + } + return *this; + } + + //! Draw an 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 c0 C-coordinate of the sprite position. + \param opacity Drawing opacity. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) return assign(sprite,false); + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); + const int + lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), + lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), + lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), + lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); + const t + *ptrs = sprite._data - + (bx?x0:0) - + (by?y0*sprite.width():0) - + (bz?z0*sprite.width()*sprite.height():0) - + (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); + const unsigned long + offX = (unsigned long)_width - lX, + soffX = (unsigned long)sprite._width - lX, + offY = (unsigned long)_width*(_height - lY), + soffY = (unsigned long)sprite._width*(sprite._height - lY), + offZ = (unsigned long)_width*_height*(_depth - lZ), + soffZ = (unsigned long)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 && lC>0) { + T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); + for (int v = 0; v=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) return assign(sprite,false); + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); + const int + lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), + lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), + lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), + lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); + const T + *ptrs = sprite._data - + (bx?x0:0) - + (by?y0*sprite.width():0) - + (bz?z0*sprite.width()*sprite.height():0) - + (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); + const unsigned long + offX = (unsigned long)_width - lX, + soffX = (unsigned long)sprite._width - lX, + offY = (unsigned long)_width*(_height - lY), + soffY = (unsigned long)sprite._width*(sprite._height - lY), + offZ = (unsigned long)_width*_height*(_depth - lZ), + soffZ = (unsigned long)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 && lC>0) { + T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); + for (int v = 0; v=1) for (int y = 0; y + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,z0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const CImg& sprite, const float opacity=1) { + return draw_image(0,sprite,opacity); + } + + //! Draw a masked image. + /** + \param sprite Sprite image. + \param mask Mask image. + \param x0 X-coordinate of the sprite position in the image instance. + \param y0 Y-coordinate of the sprite position in the image instance. + \param z0 Z-coordinate of the sprite position in the image instance. + \param c0 C-coordinate of the sprite position in the image instance. + \param mask_max_value Maximum pixel value of the mask image \c mask. + \param opacity Drawing opacity. + \note + - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. + - Dimensions along x,y and z of \p sprite and \p mask must be the same. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + if (is_empty() || !sprite || !mask) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value); + if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value); + if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth) + throw CImgArgumentException(_cimg_instance + "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data, + mask._width,mask._height,mask._depth,mask._spectrum,mask._data); + + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); + const int + lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), + lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), + lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), + lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); + const int + coff = -(bx?x0:0)-(by?y0*mask.width():0)-(bz?z0*mask.width()*mask.height():0)-(bc?c0*mask.width()*mask.height()*mask.depth():0), + ssize = mask.width()*mask.height()*mask.depth()*mask.spectrum(); + const ti *ptrs = sprite._data + coff; + const tm *ptrm = mask._data + coff; + const unsigned long + offX = (unsigned long)_width - lX, + soffX = (unsigned long)sprite._width - lX, + offY = (unsigned long)_width*(_height - lY), + soffY = (unsigned long)sprite._width*(sprite._height - lY), + offZ = (unsigned long)_width*_height*(_depth - lZ), + soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ); + if (lX>0 && lY>0 && lZ>0 && lC>0) { + T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); + for (int c = 0; c + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a text string. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color. + \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color. + \param opacity Drawing opacity. + \param font Font used for drawing text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + } + + //! Draw a text string \overloading. + /** + \note A transparent background is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font); + } + + //! Draw a text string \overloading. + /** + \note A transparent foreground is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font); + } + + //! Draw a text string \overloading. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent'). + \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent'). + \param opacity Drawing opacity. + \param font_height Height of the text font (exact match for 13,24,32,57, interpolated otherwise). + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + static CImgList font; + const unsigned int + ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57, + padding_x = font_height<=18?1:font_height<=32?2:3; + if (!font || font[0]._height!=font_height) { + font = CImgList::font(ref_height,true); + font[0].assign(1,font_height); + if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0); + } + if (is_empty()) { + if (font[0]._spectrum!=1) cimglist_for_in(font,0,255,l) font[l].channel(0); + } else if (font[0]._spectrum<_spectrum) cimglist_for_in(font,0,255,l) font[l].resize(-100,-100,1,_spectrum); + if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) { + const unsigned int __c = (unsigned int)(unsigned char)*ptrs, _c = (__c=='\t')?' ':__c; + if (_c &c = font[_c]; + if (c._height!=font_height) { + c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3); + c.resize(c._width + padding_x,-100,-100,-100,0); + } + } + if (_c+256U &c = font[_c+256]; + if (c._height!=font_height) { + c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3); + c.resize(c._width + padding_x,-100,-100,-100,0); + } + } + } + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int background_color=0, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + cimg::unused(background_color); + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp); + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); + return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp); + } + + template + CImg& _draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font) { + if (!text) return *this; + if (!font) + throw CImgArgumentException(_cimg_instance + "draw_text(): Empty specified font.", + cimg_instance); + const unsigned int text_length = (unsigned int)std::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 (unsigned int i = 0; iw) w = x; x = 0; break; + case '\t' : x+=4*font[' ']._width; break; + default : if (cw) w=x; + y+=font[0]._height; + } + assign(x0+w,y0+y,1,font[0]._spectrum,0); + if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]); + } + + int x = x0, y = y0; + CImg letter; + for (unsigned int i = 0; i& mask = (c+256)<(int)font._width?font[c+256]:font[c]; + if (foreground_color) + for (unsigned long p = 0; p<(unsigned long)letter._width*letter._height; ++p) + if (mask(p)) for (unsigned int c = 0; c=512) draw_image(x,y,letter,mask,opacity,(T)1); + else draw_image(x,y,letter,opacity); + x+=letter._width; + } + } + } + return *this; + } + + //! Draw a 2d vector field. + /** + \param flow Image of 2d vectors used as input data. + \param color Image of spectrum()-D vectors corresponding to the color of each arrow. + \param opacity Drawing opacity. + \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 is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const t2 *const color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); + } + + //! Draw a 2d vector field, using a field of colors. + /** + \param flow Image of 2d vectors used as input data. + \param color Image of spectrum()-D vectors corresponding to the color of each arrow. + \param opacity Opacity of the drawing. + \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 is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const CImg& color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + if (is_empty()) return *this; + if (!flow || flow._spectrum!=2) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).", + cimg_instance, + flow._width,flow._height,flow._depth,flow._spectrum,flow._data); + if (sampling<=0) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid sampling value %g " + "(should be >0)", + cimg_instance, + sampling); + const bool colorfield = (color._width==flow._width && color._height==flow._height && color._depth==1 && color._spectrum==_spectrum); + if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern); + float vmax,fact; + if (factor<=0) { + float m, M = (float)flow.get_norm(2).max_min(m); + vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); + if (!vmax) vmax = 1; + 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 (is_arrow) { + 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._data,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)._data,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._data,opacity,pattern); + } + } + + return *this; + } + + //! Draw a labeled horizontal axis. + /** + \param values_x Values along the horizontal axis. + \param y Y-coordinate of the horizontal axis in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const CImg& values_x, const int y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true) { + if (is_empty()) return *this; + const int yt = (y+3+font_height)<_height?(y+3):(y-2-font_height); + const int siz = (int)values_x.size()-1; + char txt[32] = { 0 }; + CImg label; + if (siz<=0) { // Degenerated case. + draw_line(0,y,_width-1,y,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x); + label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _xt = (width() - label.width())/2, + xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt; + draw_point(width()/2,y-1,color,opacity).draw_point(width()/2,y+1,color,opacity); + if (allow_zero || txt[0]!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case. + if (values_x[0]=width()-2?width()-3-label.width():_xt; + draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity); + if (allow_zero || txt[0]!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw a labeled vertical axis. + /** + \param x X-coordinate of the vertical axis in the image instance. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const int x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true) { + if (is_empty()) return *this; + int siz = (int)values_y.size()-1; + char txt[32] = { 0 }; + CImg label; + if (siz<=0) { // Degenerated case. + draw_line(x,0,x,_height-1,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_y); + label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _yt = (height() - label.height())/2, + yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt, + _xt = x - 2 - label.width(), + xt = _xt>=0?_xt:x+3; + draw_point(x-1,height()/2,color,opacity).draw_point(x+1,height()/2,color,opacity); + if (allow_zero || txt[0]!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case. + if (values_y[0]=height()?height()-1-label.height():_yt, + _xt = x - 2 - label.width(), + xt = _xt>=0?_xt:x+3; + draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); + if (allow_zero || txt[0]!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes. + /** + \param values_x Values along the X-axis. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for the X-axis. + \param pattern_y Drawing pattern for the Y-axis. + \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axes(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13, const bool allow_zero=true) { + if (is_empty()) return *this; + const CImg nvalues_x(values_x._data,values_x.size(),1,1,1,true); + const int sizx = (int)values_x.size()-1, wm1 = width()-1; + if (sizx>=0) { + float ox = (float)*nvalues_x; + for (unsigned int x = sizx?1:0; x<_width; ++x) { + const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1); + if (nx*ox<=0) { draw_axis(nx==0?x:x-1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; } + ox = nx; + } + } + const CImg nvalues_y(values_y._data,values_y.size(),1,1,1,true); + const int sizy = (int)values_y.size()-1, hm1 = height()-1; + if (sizy>0) { + float oy = (float)nvalues_y[0]; + for (unsigned int y = sizy?1:0; y<_height; ++y) { + const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1); + if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y-1,color,opacity,pattern_x,font_height,allow_zero); break; } + oy = ny; + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes \overloading. + template + CImg& draw_axes(const float x0, const float x1, const float y0, const float y1, + const tc *const color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13) { + if (is_empty()) return *this; + const bool allow_zero = (x0*x1>0) || (y0*y1>0); + const float + dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0), + px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx, + py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony; + if (x0!=x1 && y0!=y1) + draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px), + CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), + color,opacity,pattern_x,pattern_y,font_height,allow_zero); + else if (x0==x1 && y0!=y1) + draw_axis((int)x0,CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), + color,opacity,pattern_y,font_height); + else if (x0!=x1 && y0==y1) + draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0, + color,opacity,pattern_x,font_height); + return *this; + } + + //! Draw 2d grid. + /** + \param values_x X-coordinates of the vertical lines. + \param values_y Y-coordinates of the horizontal lines. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for vertical lines. + \param pattern_y Drawing pattern for horizontal lines. + **/ + template + CImg& draw_grid(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + if (values_x) cimg_foroff(values_x,x) { + const int xi = (int)values_x[x]; + if (xi>=0 && xi=0 && yi + CImg& draw_grid(const float delta_x, const float delta_y, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + CImg seqx, seqy; + if (delta_x!=0) { + const float dx = delta_x>0?delta_x:_width*-delta_x/100; + const unsigned int nx = (unsigned int)(_width/dx); + seqx = CImg::sequence(1+nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x); + } + if (delta_y!=0) { + const float dy = delta_y>0?delta_y:_height*-delta_y/100; + const unsigned int ny = (unsigned int)(_height/dy); + seqy = CImg::sequence(1+ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y); + } + return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); + } + + //! Draw 1d graph. + /** + \param data Image containing the graph values I = f(x). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + + \param plot_type Define the type of the plot: + - 0 = No plot. + - 1 = Plot using segments. + - 2 = Plot using cubic splines. + - 3 = Plot with bars. + \param vertex_type Define the type of points: + - 0 = No points. + - 1 = Point. + - 2 = Straight cross. + - 3 = Diagonal cross. + - 4 = Filled circle. + - 5 = Outlined circle. + - 6 = Square. + - 7 = Diamond. + \param ymin Lower bound of the y-range. + \param ymax Upper bound of the y-range. + \param pattern Drawing pattern. + \note + - if \c ymin==ymax==0, the y-range is computed automatically from the input samples. + **/ + template + CImg& draw_graph(const CImg& data, + const tc *const color, const float opacity=1, + const unsigned int plot_type=1, const int vertex_type=1, + const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { + if (is_empty() || _height<=1) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_graph(): Specified color is (null).", + cimg_instance); + + // Create shaded colors for displaying bar plots. + CImg color1, color2; + if (plot_type==3) { + color1.assign(_spectrum); color2.assign(_spectrum); + cimg_forC(*this,c) { color1[c] = (tc)cimg::min((float)cimg::type::max(),color[c]*1.2f); color2[c] = (tc)(color[c]*0.4f); } + } + + // Compute min/max and normalization factors. + const unsigned long + siz = data.size(), + _siz1 = siz - (plot_type!=3?1:0), + siz1 = _siz1?_siz1:1; + const unsigned int + _width1 = _width - (plot_type!=3?1:0), + width1 = _width1?_width1:1; + double m = ymin, M = ymax; + if (ymin==ymax) m = (double)data.max_min(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); + if (siz==1) { + const int Y = (int)((*data-m)/ca); + draw_line(0,Y,width()-1,Y,color,opacity,pattern); + } else for (unsigned long off = 1; off ndata(data._data,siz,1,1,1,true); + int oY = (int)((data[0]-m)/ca); + cimg_forX(*this,x) { + const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-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,color,opacity). + draw_line(oX,Y,oX,Y0,color2.data(),opacity). + draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity). + draw_line(X,Y,X,Y0,color1.data(),opacity). + draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity); + oX = X+1; + } + } break; + default : break; // No edges + } + + // Draw graph points + const unsigned int wb2 = plot_type==3?_width1/(2*siz):0; + switch (vertex_type%8) { + case 1 : { // Point + cimg_foroff(data,off) { + const int + X = off*_width1/siz1 + wb2, + Y = (int)((data[off]-m)/ca); + draw_point(X,Y,color,opacity); + } + } break; + case 2 : { // Straight Cross + cimg_foroff(data,off) { + const int + X = off*_width1/siz1 + wb2, + 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 : { // Diagonal Cross + cimg_foroff(data,off) { + const int + X = off*_width1/siz1 + wb2, + 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*_width1/siz1 + wb2, + 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*_width1/siz1 + wb2, + 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*_width1/siz1 + wb2, + 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*_width1/siz1 + wb2, + 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 points + } + return *this; + } + + //! Draw filled 3d region with the flood fill algorithm. + /** + \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 Pointer to \c spectrum() consecutive values, defining the drawing color. + \param[out] 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 is_high_connexity Tells if 8-connexity must be used (only for 2d images). + \return \c region is initialized with the binary mask of the filled region. + **/ + template + CImg& draw_fill(const int x, const int y, const int z, + const tc *const color, const float opacity, + CImg& region, const float sigma=0, + const bool is_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._data + _spectrum, *ptrs = data(x,y,z) + siz; \ + for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; 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 = data(x,y,z); \ + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \ + else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \ +} + +#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.data(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_instance + "draw_fill(): Specified color is (null).", + cimg_instance); + + region.assign(_width,_height,_depth,1,(t)0); + if (x>=0 && x=0 && y=0 && z1); + const CImg reference_color = get_vector_at(x,y,z); + CImg remaining(3,512,1,1,0); + remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z; + unsigned int posr0 = 0, posr1 = 1; + region(x,y,z) = (t)1; + const t noregion = ((t)1==(t)2)?(t)0:(t)(-1); + if (is_3d) do { // 3d version of the filling algorithm + const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++); + if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; } + bool cont, res; + unsigned int nxc = xc; + do { // X-backward + _cimg_draw_fill_set(nxc,yc,zc); + _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0); + _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,ycposr0); + else do { // 2d version of the filling algorithm + const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++); + if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; } + bool cont, res; + unsigned int nxc = xc; + do { // X-backward + _cimg_draw_fill_set(nxc,yc,0); + _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0); + _cimg_draw_fill_test_neighbor(nxc,yc+1,0,ycposr0); + if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0; + } + return *this; + } + + //! Draw filled 3d region with the flood fill algorithm \simplification. + template + CImg& draw_fill(const int x, const int y, const int z, + const tc *const color, const float opacity=1, + const float sigma=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x,y,z,color,opacity,tmp,sigma,is_high_connexity); + } + + //! Draw filled 2d region with the flood fill algorithm \simplification. + template + CImg& draw_fill(const int x, const int y, + const tc *const color, const float opacity=1, + const float sigma=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x,y,0,color,opacity,tmp,sigma,is_high_connexity); + } + + //! Draw a random plasma texture. + /** + \param alpha Alpha-parameter. + \param beta Beta-parameter. + \param scale Scale-parameter. + \note Use the mid-point algorithm to render. + **/ + CImg& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) { + if (is_empty()) return *this; + const int w = width(), h = height(); + const Tfloat m = (Tfloat)cimg::type::min(), M = (Tfloat)cimg::type::max(); + cimg_forZC(*this,z,c) { + CImg ref = get_shared_slice(z,c); + for (int d=1<1; d>>=1) { + const int d2 = d>>1; + const float r = alpha*d + beta; + for (int y0=0; y0M?M:val); + } + for (int y=-d2; yM?M:val); + } + for (int y0=0; y0M?M:val); + } + for (int y=-d2; yM?M:val); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2d fractal. + /** + \param x0 X-coordinate of the upper-left pixel. + \param y0 Y-coordinate of the upper-left pixel. + \param x1 X-coordinate of the lower-right pixel. + \param y1 Y-coordinate of the lower-right pixel. + \param colormap Colormap. + \param opacity Drawing opacity. + \param z0r Real part of the upper-left fractal vertex. + \param z0i Imaginary part of the upper-left fractal vertex. + \param z1r Real part of the lower-right fractal vertex. + \param z1i Imaginary part of the lower-right fractal vertex. + \param iteration_max Maximum number of iterations for each estimated point. + \param is_normalized_iteration Tells if iterations are normalized. + \param is_julia_set Tells if the Mandelbrot or Julia set is rendered. + \param param_r Real part of the Julia set parameter. + \param param_i Imaginary part of the Julia set parameter. + \note Fractal rendering is done by the Escape Time Algorithm. + **/ + template + CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, + const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + if (is_empty()) return *this; + CImg palette; + if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true); + if (palette && palette._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0); + unsigned int iteration = 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 (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; } + else { zr = param_r; zi = param_i; cr = x; ci = y; } + for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) { + const double temp = zr*zr - zi*zi + cr; + zi = 2*zr*zi + ci; + zr = temp; + } + if (iteration>iteration_max) { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity); + } + } else if (is_normalized_iteration) { + const float + normz = (float)cimg::abs(zr*zr+zi*zi), + niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2); + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } else { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2d fractal \overloading. + template + CImg& draw_mandelbrot(const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + return draw_mandelbrot(0,0,_width-1,_height-1,colormap,opacity, + z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i); + } + + //! Draw a 1d gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param sigma Standard variation of the gaussian distribution. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float sigma, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned long whd = (unsigned long)_width*_height*_depth; + const tc *col = color; + cimg_forX(*this,x) { + const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2); + T *ptrd = data(x,0,0,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 2d gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param yc Y-coordinate of the gaussian center. + \param tensor Covariance matrix (must be 2x2). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + typedef typename CImg::Tfloat tfloat; + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned long whd = (unsigned long)_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)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = data(x,y,0,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + ++dx; + } + ++dy; + } + return *this; + } + + //! Draw a 2d gaussian function \overloading. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + const double + a = r1*ru*ru + r2*rv*rv, + b = (r1-r2)*ru*rv, + c = r1*rv*rv + r2*ru*ru; + const CImg tensor(2,2,1,1, a,b,b,c); + return draw_gaussian(xc,yc,tensor,color,opacity); + } + + //! Draw a 2d gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw a 3d gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename CImg::Tfloat tfloat; + if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned long whd = (unsigned long)_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)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = data(x,y,z,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 3d gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw a 3d object. + /** + \param x0 X-coordinate of the 3d object position + \param y0 Y-coordinate of the 3d object position + \param z0 Z-coordinate of the 3d object position + \param vertices Image Nx3 describing 3d point coordinates + \param primitives List of P primitives + \param colors List of P color (or textures) + \param opacities Image or list of P opacities + \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param is_double_sided Tells if object faces have two sides or are oriented. + \param focale length of the focale (0 for parallel projection) + \param lightx X-coordinate of the light + \param lighty Y-coordinate of the light + \param lightz Z-coordinate of the light + \param specular_lightness Amount of specular light. + \param specular_shininess Shininess of the object + **/ + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); + } + + //! Draw a 3d object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1); + } +#endif + + //! Draw a 3d object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); + } + + //! Draw a 3d object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1); + } +#endif + + //! Draw a 3d object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,CImg::empty()); + } + + //! Draw a 3d object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,zbuffer); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,zbuffer); + } +#endif + + template + static float __draw_object3d(const CImgList& opacities, const unsigned int n_primitive, CImg& opacity) { + if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; } + if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); } + opacity.assign(opacities[n_primitive],true); + return 1.0f; + } + + template + static float __draw_object3d(const CImg& opacities, const unsigned int n_primitive, CImg& opacity) { + opacity.assign(); + return n_primitive>=opacities._width?1.0f:(float)opacities[n_primitive]; + } + + template + CImg& _draw_object3d(void *const pboard, CImg& zbuffer, + const float X, const float Y, const float Z, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float sprite_scale) { + typedef typename cimg::superset2::type tpfloat; + if (is_empty() || !vertices || !primitives) return *this; + char error_message[1024] = { 0 }; + if (!vertices.is_object3d(primitives,colors,opacities,false,error_message)) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid specified 3d object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message); +#ifndef cimg_use_board + if (pboard) return *this; +#endif + const float + nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)), + nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess), + nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), + nsl2 = 1 - 2*nsl1*nspec, + nsl3 = nspec2 - nsl1 - nsl2; + + // Create light texture for phong-like rendering. + CImg light_texture; + if (render_type==5) { + if (colors._width>primitives._width) { + static CImg default_light_texture; + static const tc *lptr = 0; + static tc ref_values[64] = { 0 }; + const CImg& img = colors.back(); + bool is_same_texture = (lptr==img._data); + if (is_same_texture) + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum)) { is_same_texture = false; break; } + if (!is_same_texture || default_light_texture._spectrum<_spectrum) { + (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum); + lptr = colors.back().data(); + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum); + } + light_texture.assign(default_light_texture,true); + } else { + static CImg default_light_texture; + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; + if (!default_light_texture || + lightx!=olightx || lighty!=olighty || lightz!=olightz || + specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) { + default_light_texture.assign(512,512); + const float + dlx = lightx - X, + dly = lighty - Y, + dlz = lightz - Z, + nl = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz), + nlx = default_light_texture._width/2*(1 + dlx/nl), + nly = default_light_texture._height/2*(1 + dly/nl), + white[] = { 1 }; + default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white); + cimg_forXY(default_light_texture,x,y) { + const float factor = default_light_texture(x,y); + if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3); + } + default_light_texture.resize(-100,-100,1,_spectrum); + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; + } + light_texture.assign(default_light_texture,true); + } + } + + // Compute 3d to 2d projection. + CImg projections(vertices._width,2); + tpfloat parallzmin = cimg::type::max(); + const float absfocale = focale?cimg::abs(focale):0; + if (absfocale) cimg_forX(projections,l) { // Perspective projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + const tpfloat projectedz = z + Z + absfocale; + projections(l,1) = Y + absfocale*y/projectedz; + projections(l,0) = X + absfocale*x/projectedz; + } else cimg_forX(projections,l) { // Parallel projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + if (z visibles(primitives._width); + CImg zrange(primitives._width); + unsigned int nb_visibles = 0; + const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)primitive(0); + const tpfloat z0 = Z + vertices(i0,2); + if (z0>zmin) { + 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); + const tpfloat + Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)), + Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)), + Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)), + _zc = Z + Zc, + zc = _zc + _focale, + xc = X + Xc*(absfocale?absfocale/zc:1), + yc = Y + Yc*(absfocale?absfocale/zc:1), + radius = 0.5f*std::sqrt(cimg::sqr(vertices(i1,0) - vertices(i0,0)) + + cimg::sqr(vertices(i1,1) - vertices(i0,1)) + + cimg::sqr(vertices(i1,2) - vertices(i0,2)))*(absfocale?absfocale/zc:1), + xm = xc - radius, + ym = yc - radius, + xM = xc + radius, + yM = yc + radius; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) { + visibles(nb_visibles) = (unsigned int)l; + zrange(nb_visibles++) = _zc; + } + } break; + case 2 : // Segment + case 6 : { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2); + tpfloat xm, xM, ym, yM; + if (x0=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { + visibles(nb_visibles) = (unsigned int)l; + zrange(nb_visibles++) = (z0 + z1)/2; + } + } 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 tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (y0yM) yM = y2; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0); + if (is_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 tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2), + x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0); + if (is_double_sided || d<0) { + visibles(nb_visibles) = (unsigned int)l; + zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4; + } + } + } break; + default : + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid primitive[%u] with size %u " + "(should have size 1,2,3,4,5,6,9 or 12).", + cimg_instance, + l,primitive.size()); + } + } + + if (nb_visibles<=0) return *this; + CImg permutations; + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3 : { // Flat Shading + lightprops.assign(nb_visibles); + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(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 = (tpfloat)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 = (tpfloat)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 vertices_normals(vertices._width,3,1,1,0); + for (unsigned int l = 0; l& primitive = primitives[visibles(l)]; + const unsigned int psize = primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + rectangle_flag = (psize==4) || (psize==12); + if (triangle_flag || rectangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = rectangle_flag?(unsigned int)primitive(3):0; + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(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 = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)), + nx = nnx/norm, + ny = nny/norm, + nz = nnz/norm; + vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz; + vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz; + vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz; + if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; } + } + } + + if (is_double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) { + vertices_normals(p,0) = -vertices_normals(p,0); + vertices_normals(p,1) = -vertices_normals(p,1); + vertices_normals(p,2) = -vertices_normals(p,2); + } + + if (render_type==4) { + lightprops.assign(vertices._width); + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + lx = X + vertices(l,0) - lightx, + ly = Y + vertices(l,1) - lighty, + lz = Z + vertices(l,2) - lightz, + nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), + factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0); + lightprops[l] = 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(vertices._width,2); + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + nnx = nx/norm, + nny = ny/norm; + lightprops(l,0) = lw2*(1 + nnx); + lightprops(l,1) = lh2*(1 + nny); + } + } + } break; + } + + // Draw visible primitives + const CImg default_color(1,_spectrum,1,1,(tc)200); + typedef typename to::value_type _to; + CImg<_to> _opacity; + + for (unsigned int l = 0; l& primitive = primitives[n_primitive]; + const CImg + &__color = n_primitive(), + _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?__color.get_resize(-100,-100,-100,_spectrum,0):CImg(), + &color = _color?_color:(__color?__color:default_color); + const tc *const pcolor = color._data; + const float opacity = __draw_object3d(opacities,n_primitive,_opacity); + +#ifdef cimg_use_board + LibBoard::Board &board = *(LibBoard::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 (_opacity.is_empty()) { // Scalar opacity. + + if (color.size()==_spectrum) { // Colored point. + draw_point(x0,y0,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillCircle((float)x0,height()-(float)y0,0); + } +#endif + } else { // Sprite. + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(color._width*factor), + _sh = (unsigned int)(color._height*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && (nx0+(int)sw/2>=0 || nx0-(int)sw/2=0 || ny0-(int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + draw_image(nx0,ny0,sprite,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::None); + board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh); + } +#endif + } + } + } else { // Opacity mask. + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(cimg::max(color._width,_opacity._width)*factor), + _sh = (unsigned int)(cimg::max(color._height,_opacity._height)*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && (nx0+(int)sw/2>=0 || nx0-(int)sw/2=0 || ny0-(int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + const CImg<_to> + _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?_opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(), + &nopacity = _nopacity?_nopacity:_opacity; + draw_image(nx0,ny0,sprite,nopacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::None); + board.drawRectangle((float)nx0,height()-(float)ny0,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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity); + else draw_line(x0,y0,x1,y1,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1); + } +#endif + } else { + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(float)y1,0); + } +#endif + } + } break; + case 5 : { // Colored sphere + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const float + Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)), + Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)), + Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)), + zc = Z + Zc + _focale, + xc = X + Xc*(absfocale?absfocale/zc:1), + yc = Y + Yc*(absfocale?absfocale/zc:1), + radius = 0.5f*std::sqrt(cimg::sqr(vertices(n1,0) - vertices(n0,0)) + + cimg::sqr(vertices(n1,1) - vertices(n0,1)) + + cimg::sqr(vertices(n1,2) - vertices(n0,2)))*(absfocale?absfocale/zc:1); + switch (render_type) { + case 0 : + draw_point((int)xc,(int)yc,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillCircle(xc,height()-yc,0); + } +#endif + break; + case 1 : + draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.setFillColor(LibBoard::Color::None); + board.drawCircle(xc,height()-yc,radius); + } +#endif + break; + default : + draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillCircle(xc,height()-yc,radius); + } +#endif + break; + } + } break; + case 6 : { // Textured line + if (!__color) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for line primitive [%u].", + cimg_instance,n_primitive); + 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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + } +#endif + } else { + draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(float)y1,0); + board.drawCircle((float)x2,height()-(float)y2,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity). + draw_line(x1,y1,x2,y2,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2); + board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)); + else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,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)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + } +#endif + break; + case 4 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0), + (float)x1,height()-(float)y1,lightprops(n1), + (float)x2,height()-(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,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1)))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, + (float)x1,height()-(float)y1,l1, + (float)x2,height()-(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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale; + + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity). + draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(float)y1,0); + board.drawCircle((float)x2,height()-(float)y2,0); + board.drawCircle((float)x3,height()-(float)y3,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity). + draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3); + board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l)); + else + _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)). + _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,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)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(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,pcolor,lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0, + (float)x1,height()-(float)y1,lightprop1, + (float)x2,height()-(float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0, + (float)x2,height()-(float)y2,lightprop2, + (float)x3,height()-(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,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, + (float)x1,height()-(float)y1,l1, + (float)x2,height()-(float)y2,l2); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, + (float)x2,height()-(float)y2,l2, + (float)x3,height()-(float)y3,l3); + } +#endif + } break; + } + } break; + case 9 : { // Textured triangle + if (!__color) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for triangle primitive [%u].", + cimg_instance,n_primitive); + 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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(float)y1,0); + board.drawCircle((float)x2,height()-(float)y2,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2); + board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(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,opacity); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(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,opacity,lightprops(l)); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,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)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(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),opacity); + 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),opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0), + (float)x1,height()-(float)y1,lightprops(n1), + (float)x2,height()-(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), + opacity); + 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), + opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1)))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2); + } +#endif + break; + } + } break; + case 12 : { // Textured quadrangle + if (!__color) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for quadrangle primitive [%u].", + cimg_instance,n_primitive); + 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 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale; + + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opacity). + draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawCircle((float)x0,height()-(float)y0,0); + board.drawCircle((float)x1,height()-(float)y1,0); + board.drawCircle((float)x2,height()-(float)y2,0); + board.drawCircle((float)x3,height()-(float)y3,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3); + board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(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,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(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,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,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)(opacity*255)); + board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(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,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0, + (float)x1,height()-(float)y1,lightprop1, + (float)x2,height()-(float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0, + (float)x2,height()-(float)y2,lightprop2, + (float)x3,height()-(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,opacity). + 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,opacity); + 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,opacity). + 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,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, + (float)x1,height()-(float)y1,l1, + (float)x2,height()-(float)y2,l2); + board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, + (float)x2,height()-(float)y2,l2, + (float)x3,height()-(float)y3,l3); + } +#endif + } break; + } + } break; + } + } + return *this; + } + + //@} + //--------------------------- + // + //! \name Data Input + //@{ + //--------------------------- + + //! Launch simple interface to select a shape from an image. + /** + \param disp Display window to use. + \param feature_type Type of feature to select. Can be { 0=point | 1=line | 2=rectangle | 3=ellipse }. + \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. + **/ + CImg& select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0) { + return get_select(disp,feature_type,XYZ).move_to(*this); + } + + //! Simple interface to select a shape from an image \overloading. + CImg& select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0) { + return get_select(title,feature_type,XYZ).move_to(*this); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0) const { + return _get_select(disp,0,feature_type,XYZ,0,0,0,true,false); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0) const { + CImgDisplay disp; + return _get_select(disp,title,feature_type,XYZ,0,0,0,true,false); + } + + CImg _get_select(CImgDisplay &disp, const char *const title, + const unsigned int feature_type, unsigned int *const XYZ, + const int origX, const int origY, const int origZ, + const bool reset_view3d, + const bool force_display_z_coord) const { + if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + } else if (title) disp.set_title("%s",title); + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).set_wheel(); + + unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + + int area = 0, starting_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, X3d = -1, Y3d = -1, + oX = X, oY = Y, oZ = Z, oX3d = X3d, oY3d = -1; + unsigned int old_button = 0, key = 0; + + bool shape_selected = false, text_down = false; + static CImg pose3d; + static bool is_view3d = false; + if (reset_view3d) { pose3d.assign(); is_view3d = false; } + CImg points3d, opacities3d, sel_opacities3d; + CImgList primitives3d, sel_primitives3d; + CImgList colors3d, sel_colors3d; + CImg visu, visu0, view3d; + 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<0?-1:mx*(width()+(depth()>1?depth():0))/disp.width(), + mY = my<0?-1:my*(height()+(depth()>1?depth():0))/disp.height(); + area = 0; + if (mX>=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; } + if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; + if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0; + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break; + case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).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.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyV : is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp); + visu0.save(filename); + visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + } + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp); + save(filename); + visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + disp.set_key(key,false); key = 0; + } break; + } + + switch (area) { + + case 0 : // When mouse is out of image range. + mx = my = X = Y = Z = -1; + break; + + case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections. + if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step). + if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign(); + X1 = X; Y1 = Y; Z1 = Z; + } + if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes). + switch (starting_area) { + case 1 : if (Z1!=Z) visu0.assign(); Z1 = Z; break; + case 2 : if (Y1!=Y) visu0.assign(); Y1 = Y; break; + case 3 : if (X1!=X) visu0.assign(); X1 = X; break; + } + } + if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume. + if (phase) { + if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign(); + X1 = X; Y1 = Y; Z1 = Z; + } else { + if (_depth>1 && (X0!=X || Y0!=Y || Z0!=Z)) visu0.assign(); + X0 = X; Y0 = Y; Z0 = Z; + } + } + if (disp.button()&4) { // Reset positions. + oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = area = clicked_area = starting_area = 0; visu0.assign(); + } + if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel). + if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() && + !disp.is_keyALT() && !disp.is_keyALTGR()) { + switch (area) { + case 1 : + if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel()); + visu0.assign(); break; + case 2 : + if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel()); + visu0.assign(); break; + case 3 : + if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel()); + visu0.assign(); break; + } + disp.set_wheel(); + } else key = ~0U; + } + if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released. + switch (phase) { + case 0 : + if (area==clicked_area) { + X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; starting_area = area; ++phase; + } break; + case 1 : + if (area==starting_area) { + X1 = X; Y1 = Y; Z1 = Z; ++phase; + } else if (!(disp.button()&1)) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu0.assign(); } + break; + case 2 : ++phase; break; + } + old_button = disp.button()&1; + } + break; + + case 4 : // When mouse is over the 3d view. + if (is_view3d && points3d) { + X3d = mx - _width*disp.width()/(_width+(_depth>1?_depth:0)); + Y3d = my - _height*disp.height()/(_height+(_depth>1?_depth:0)); + if (oX3d<0) { oX3d = X3d; oY3d = Y3d; } + if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } // Left + right buttons: reset. + else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate. + const float + R = 0.45f*cimg::min(view3d._width,view3d._height), + R2 = R*R, + u0 = (float)(oX3d-view3d.width()/2), + v0 = (float)(oY3d-view3d.height()/2), + u1 = (float)(X3d-view3d.width()/2), + v1 = (float)(Y3d-view3d.height()/2), + n0 = (float)std::sqrt(u0*u0+v0*v0), + n1 = (float)std::sqrt(u1*u1+v1*v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)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)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)std::sqrt(u*u+v*v+w*w), + alpha = (float)std::asin(n/R2); + pose3d.draw_image(CImg::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2)); + view3d.assign(); + } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom. + pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign(); + } + if (disp.wheel()) { // Wheel: zoom + pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); + } + if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift. + pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign(); + } + oX3d = X3d; oY3d = Y3d; + } + mx = my = X = Y = Z = -1; + break; + } + + if (phase) { + if (!feature_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>=width()) X0 = width() - 1; + if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1; + if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1; + if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1; + if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1; + if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1; + + // Draw visualization image on the display + if (oX!=X || oY!=Y || oZ!=Z || !visu0 || (_depth>1 && !view3d)) { + + if (!visu0) { // Create image of projected planes. + CImg tmp, tmp0; + if (_depth!=1) { + tmp0 = get_projections2d(phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0); + tmp = tmp0.get_channels(0,cimg::min(2,spectrum() - 1)); + } else tmp = get_channels(0,cimg::min(2,spectrum() - 1)); + + if (cimg::type::is_float()) { // Replace +-inf values. + Tuchar m = cimg::type::max(), M = cimg::type::min(); + bool is_inf = false; + cimg_for(tmp,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) is_inf = true; + else if (*ptrM) M = *ptr; + if (is_inf) { + const Tuchar + minf = m - (M-m)*20 - 1, + pinf = M + (M-m)*20 + 1; + cimg_for(tmp,ptr,Tuchar) if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?minf:pinf; + } + } + + switch (old_normalization) { + case 0 : tmp.move_to(visu0); break; + case 1 : tmp.normalize(0,255).move_to(visu0); break; + case 2 : { + const float m = disp._min, M = disp._max; + ((tmp-=m)*=255.0f/(M-m>0?M-m:1)).move_to(visu0); + } + case 3 : + if (cimg::type::is_float()) (tmp.normalize(0,255)).move_to(visu0); + else { + const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); + ((tmp-=m)*=255.0f/(M-m)).move_to(visu0); + } break; + } + visu0.resize(disp); + view3d.assign(); + points3d.assign(); + } + + if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images. + const unsigned int + _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width+_depth),1,1), + _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height+_depth),1,1), + x3d = _x3d>=visu0._width?visu0._width-1:_x3d, + y3d = _y3d>=visu0._height?visu0._height-1:_y3d; + CImg(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d); + if (!points3d) { + get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d); + points3d.append(CImg(8,3,1,1, + 0,_width-1,_width-1,0,0,_width-1,_width-1,0, + 0,0,_height-1,_height-1,0,0,_height-1,_height-1, + 0,0,0,0,_depth-1,_depth-1,_depth-1,_depth-1),'x'); + CImg::vector(12,13).move_to(primitives3d); CImg::vector(13,14).move_to(primitives3d); + CImg::vector(14,15).move_to(primitives3d); CImg::vector(15,12).move_to(primitives3d); + CImg::vector(16,17).move_to(primitives3d); CImg::vector(17,18).move_to(primitives3d); + CImg::vector(18,19).move_to(primitives3d); CImg::vector(19,16).move_to(primitives3d); + CImg::vector(12,16).move_to(primitives3d); CImg::vector(13,17).move_to(primitives3d); + CImg::vector(14,18).move_to(primitives3d); CImg::vector(15,19).move_to(primitives3d); + colors3d.insert(12,CImg::vector(255,255,255)); + opacities3d.assign(primitives3d.width(),1,1,1,0.5f); + if (!phase) { + opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f; + sel_primitives3d.assign(); + sel_colors3d.assign(); + sel_opacities3d.assign(); + } else { + if (feature_type==2) { + points3d.append(CImg(8,3,1,1, + X0,X1,X1,X0,X0,X1,X1,X0, + Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, + Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); + sel_primitives3d.assign(); + CImg::vector(20,21).move_to(sel_primitives3d); CImg::vector(21,22).move_to(sel_primitives3d); + CImg::vector(22,23).move_to(sel_primitives3d); CImg::vector(23,20).move_to(sel_primitives3d); + CImg::vector(24,25).move_to(sel_primitives3d); CImg::vector(25,26).move_to(sel_primitives3d); + CImg::vector(26,27).move_to(sel_primitives3d); CImg::vector(27,24).move_to(sel_primitives3d); + CImg::vector(20,24).move_to(sel_primitives3d); CImg::vector(21,25).move_to(sel_primitives3d); + CImg::vector(22,26).move_to(sel_primitives3d); CImg::vector(23,27).move_to(sel_primitives3d); + } else { + points3d.append(CImg(2,3,1,1, + X0,X1, + Y0,Y1, + Z0,Z1),'x'); + sel_primitives3d.assign(CImg::vector(20,21)); + } + sel_colors3d.assign(sel_primitives3d._width,CImg::vector(255,255,255)); + sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); + } + points3d.shift_object3d(-0.5f*_width,-0.5f*_height,-0.5f*_depth).resize_object3d(); + points3d*=0.75f*cimg::min(view3d._width,view3d._height); + } + + if (!pose3d) CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); + CImg zbuffer3d(view3d._width,view3d._height,1,1,0); + const CImg rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d; + if (sel_primitives3d) + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d, + 2,true,500,0,0,0,0,0,zbuffer3d); + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,primitives3d,colors3d,opacities3d, + 2,true,500,0,0,0,0,0,zbuffer3d); + visu0.draw_image(x3d,y3d,view3d); + } + visu = visu0; + + const int d = (_depth>1)?_depth:0; + if (phase) switch (feature_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,background_color,0.9f,30,5,0x55555555). + draw_arrow(x0,y0,x1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA); + 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.9f,30,5,0x55555555). + draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0x55555555). + draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA). + draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0xAAAAAAAA); + } + } break; + case 2 : { + const int + x0 = (X0=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f). + draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,~0U); + + if (_depth>1) { + if (y1-y0>=4 && zx1-zx0>=4) visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f). + draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,~0U); + if (x1-x0>=4 && zy1-zy0>=4) visu.draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f). + draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,~0U); + } + } + + if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false; + if (!feature_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X1 || force_display_z_coord) cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z); + else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+X,origY+Y); + char *ctext = text + std::strlen(text), *const ltext = text + 512; + for (unsigned int c = 0; c<_spectrum && ctext::format(),cimg::type::format((*this)(X,Y,Z,c))); + ctext = text + std::strlen(text); + *(ctext++) = ' '; *ctext = 0; + } + std::strcpy(text + std::strlen(text),"] "); + } + } else switch (feature_type) { + case 1 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ); + if (_depth>1 || force_display_z_coord) cimg_snprintf(text,sizeof(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_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ", + origX+X0,origY+Y0,origX+X1,origY+Y1,norm); + } break; + case 2 : + if (_depth>1 || force_display_z_coord) cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ", + origX+(X01 || force_display_z_coord) cimg_snprintf(text,sizeof(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_snprintf(text,sizeof(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.height()-13:0,text,foreground_color,background_color,0.7f,13); + disp.display(visu).wait(); + } else if (!shape_selected) disp.wait(); + if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } + } + + // Return result + CImg res(1,feature_type==0?3: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 (feature_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 (feature_type) { + case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; + case 3 : res[3] = cimg::abs(X1-X0); res[4] = cimg::abs(Y1-Y0); res[5] = cimg::abs(Z1-Z0); // keep no break here! + default : res[0] = X0; res[1] = Y0; res[2] = Z0; + } + } + disp.set_button(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + if (key!=~0U) disp.set_key(key); + return res; + } + + //! Select sub-graph in a graph. + CImg get_select_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "select_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type()); + const unsigned long siz = (unsigned long)_width*_height*_depth; + const unsigned int old_normalization = disp.normalization(); + disp.show().set_button().set_wheel()._normalization = 0; + + double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; + if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; } + 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 colormap; + if (odimv!=_spectrum) { + odimv = _spectrum; + colormap = CImg(3,_spectrum,1,1,120).noise(70,1); + if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } + else { + colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10; + if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; } + if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; } + } + } + + CImg visu0, visu, graph, text, axes; + int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; + const unsigned int one = plot_type==3?0:1; + unsigned int okey = 0, obutton = 0; + char message[1024] = { 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.width(),disp.height(),1,3,220); + const int gdimx = disp.width() - 32, gdimy = disp.height() - 32; + if (gdimx>0 && gdimy>0) { + graph.assign(gdimx,gdimy,1,3,255); + if (siz<32) { if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,false,true,black,0.2f,0x33333333,0x33333333); } + else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); + cimg_forC(*this,c) graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==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)std::pow(10.0,(int)std::log10(dx?dx:1)-2.0), + py = (float)std::pow(10.0,(int)std::log10(dy?dy:1)-2.0); + const CImg + seqx = dx<=0?CImg::vector(nxmin):CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px), + seqy = CImg::sequence(1 + gdimy/60,nymax,nymin).round(py); + + const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0); + axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero); + if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray,1,~0U,13,allow_zero); + if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero); + if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero); + if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray,1,~0U,13,allow_zero); + + cimg_for3x3(axes,x,y,0,0,I,unsigned char) + if (Icc) { + if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0; + else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3); + } + else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+511)/3; + + 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,13).resize(-100,-100,1,3); + visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text); + text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3); + visu0.draw_image(1,(visu0.height()-text.height())/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.width()-32)/cimg::max(1U,siz-one), + sx1 = 15 + (nx1+1)*(visu.width()-32)/cimg::max(1U,siz-one), + 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.height()-17,gray,0.5f). + draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU). + draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU); + } + if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) + cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, + (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), + (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),(double)(*this)(x,0,0,_spectrum-1)); + else { + cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( ",x,cx); + cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); + std::sprintf(message + std::strlen(message),")"); + } + if (x0>=0 && x1>=0) { + const unsigned 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)/cimg::max(1U,siz-1), + cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/cimg::max(1U,siz-1), + cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32), + cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32); + if (y0>=0 && y1>=0) + std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",x0,cx0,cy0,x1+one,cx1,cy1); + else + std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",x0,cx0,x1+one,cx1); + } + text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); + visu.draw_image((visu.width()-text.width())/2,1,~text); + } + visu.display(disp); + } + + // Test keys. + switch (okey = key) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).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.set_key(key,false); okey = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(640,480,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp); + screen.save(filename); + screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + char filename[32] = { 0 }; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp); + save(filename); + screen.draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp); + } + disp.set_key(key,false); 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)(siz-one)/(disp.width()-32), + cx = mx<0?0:(mx>=(int)(siz-one)?(int)(siz-1-one):mx), + my = mouse_y - 16, + cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-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 = x1>=0?cx:-1; 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 = old_normalization; + if (x1>=0 && x1(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1); + } + + //! Load image from a file. + /** + \param filename Filename, as a C-string. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load the file as a .cimg or .cimgz file. + **/ + CImg& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load(): Specified filename is (null).", + cimg_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + char filename_local[1024] = { 0 }; + load(cimg::load_network_external(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + 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); + else if (!cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) load_dlm(filename); + + // 2d binary formats + else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); + else if (!cimg::strcasecmp(ext,"png")) load_png(filename); + else if (!cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"pnm") || + !cimg::strcasecmp(ext,"pbm") || + !cimg::strcasecmp(ext,"pnk")) load_pnm(filename); + else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); + else 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); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + + // 3d binary formats + else if (!cimg::strcasecmp(ext,"dcm") || + !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) load_analyze(filename); + else if (!cimg::strcasecmp(ext,"par") || + !cimg::strcasecmp(ext,"rec")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename); + else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) return load_cimg(filename); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + + // Image sequences + else 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,"ogg") || + !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); + else throw CImgIOException("CImg<%s>::load()", + pixel_type()); + } catch (CImgIOException&) { + std::FILE *file = 0; + try { + file = cimg::fopen(filename,"rb"); + } catch (CImgIOException&) { + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load(): Failed to open file '%s'.", + cimg_instance, + filename); + } + + try { + const char *const f_type = cimg::file_type(file,filename); + std::fclose(file); + if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); + else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename); + else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(f_type,"png")) load_png(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); + else throw CImgIOException("CImg<%s>::load()", + pixel_type()); + } catch (CImgIOException&) { + try { + load_other(filename); + } catch (CImgIOException&) { + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + } + cimg::exception_mode() = omode; + return *this; + } + + //! Load image from a file \newinstance. + static CImg get_load(const char *const filename) { + return CImg().load(filename); + } + + //! Load image from an ascii file. + /** + \param filename Filename, as a C -string. + **/ + CImg& load_ascii(const char *const filename) { + return _load_ascii(0,filename); + } + + //! Load image from an ascii file \inplace. + static CImg get_load_ascii(const char *const filename) { + return CImg().load_ascii(filename); + } + + //! Load image from an ascii file \overloading. + CImg& load_ascii(std::FILE *const file) { + return _load_ascii(file,0); + } + + //! Loadimage from an ascii file \newinstance. + static CImg get_load_ascii(std::FILE *const file) { + return CImg().load_ascii(file); + } + + CImg& _load_ascii(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_ascii(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char line[256] = { 0 }; + int err = std::fscanf(nfile,"%255[^\n]",line); + unsigned int dx = 0, dy = 1, dz = 1, dc = 1; + std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); + err = std::fscanf(nfile,"%*[^0-9.eE+-]"); + if (!dx || !dy || !dz || !dc) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_ascii(): Invalid ascii header in file '%s', image dimensions are set to (%u,%u,%u,%u).", + cimg_instance, + filename?filename:"(FILE*)",dx,dy,dz,dc); + } + assign(dx,dy,dz,dc); + const unsigned long siz = size(); + unsigned long off = 0; + double val; + T *ptr = _data; + for (err = 1, off = 0; off& load_dlm(const char *const filename) { + return _load_dlm(0,filename); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(const char *const filename) { + return CImg().load_dlm(filename); + } + + //! Load image from a DLM file \overloading. + CImg& load_dlm(std::FILE *const file) { + return _load_dlm(file,0); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(std::FILE *const file) { + return CImg().load_dlm(file); + } + + CImg& _load_dlm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_dlm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + char delimiter[256] = { 0 }, tmp[256] = { 0 }; + unsigned int cdx = 0, dx = 0, dy = 0; + int err = 0; + double val; + assign(256,256); + while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) { + if (err>0) (*this)(cdx++,dy) = (T)val; + if (cdx>=_width) resize(3*_width/2,_height,1,1,0); + char c = 0; + if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { + dx = cimg::max(cdx,dx); + if (++dy>=_height) resize(_width,3*_height/2,1,1,0); + cdx = 0; + } + } + if (cdx && err==1) { dx = cdx; ++dy; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_dlm(): Invalid DLM file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a BMP file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_bmp(const char *const filename) { + return _load_bmp(0,filename); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(const char *const filename) { + return CImg().load_bmp(filename); + } + + //! Load image from a BMP file \overloading. + CImg& load_bmp(std::FILE *const file) { + return _load_bmp(file,0); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(std::FILE *const file) { + return CImg().load_bmp(file); + } + + CImg& _load_bmp(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_bmp(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char header[64] = { 0 }; + cimg::fread(header,54,nfile); + if (*header!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid BMP file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // 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); + + if (!file_size || file_size==offset) { + std::fseek(nfile,0,SEEK_END); + file_size = (int)std::ftell(nfile); + std::fseek(nfile,54,SEEK_SET); + } + + const int + cimg_iobuffer = 12*1024*1024, + dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), + align_bytes = (4-dx_bytes%4)%4, + buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset); + + CImg colormap; + if (bpp<16) { if (!nb_colors) nb_colors = 1<0) std::fseek(nfile,xoffset,SEEK_CUR); + + CImg buffer; + if (buf_size=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + unsigned char mask = 0x80, val = 0; + cimg_forX(*this,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(colormap._data + (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_bytes; + } + } break; + case 4 : { // 16 colors + for (int y = height()-1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + 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)); + const unsigned char *col = (unsigned char*)(colormap._data + 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_bytes; + } + } break; + case 8 : { // 256 colors + for (int y = height()-1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + cimg_forX(*this,x) { + const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + } + ptrs+=align_bytes; + } + } break; + case 16 : { // 16 bits colors + for (int y = height()-1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + 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_bytes; + } + } break; + case 24 : { // 24 bits colors + for (int y = height()-1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + } + ptrs+=align_bytes; + } + } break; + case 32 : { // 32 bits colors + for (int y = height()-1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + 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_bytes; + } + } break; + } + if (dy<0) mirror('y'); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a JPEG file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_jpeg(const char *const filename) { + return _load_jpeg(0,filename); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(const char *const filename) { + return CImg().load_jpeg(filename); + } + + //! Load image from a JPEG file \overloading. + CImg& load_jpeg(std::FILE *const file) { + return _load_jpeg(file,0); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(std::FILE *const file) { + return CImg().load_jpeg(file); + } + + // Custom error handler for libjpeg. +#ifdef cimg_use_jpeg + struct _cimg_error_mgr { + struct jpeg_error_mgr original; + jmp_buf setjmp_buffer; + char message[JMSG_LENGTH_MAX]; + }; + + typedef struct _cimg_error_mgr *_cimg_error_ptr; + + METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) { + _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point + (*cinfo->err->format_message)(cinfo,c_err->message); + jpeg_destroy(cinfo); // Clean memory and temp files. + longjmp(c_err->setjmp_buffer,1); + } +#endif + + CImg& _load_jpeg(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_jpeg(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException(_cimg_instance + "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.", + cimg_instance); + else return load_other(filename); +#else + + struct jpeg_decompress_struct cinfo; + struct _cimg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr.original); + jerr.original.error_exit = _cimg_jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) { // JPEG error + throw CImgIOException(_cimg_instance + "load_jpeg(): Error message returned by libjpeg: %s.", + cimg_instance,jerr.message); + } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + 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) { + if (!file) { + cimg::fclose(nfile); + return load_other(filename); + } else + throw CImgIOException(_cimg_instance + "load_jpeg(): Failed to load JPEG data from file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + } + CImg buffer(cinfo.output_width*cinfo.output_components); + JSAMPROW row_pointer[1]; + assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); + T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, *ptr_a = _data + 3UL*_width*_height; + while (cinfo.output_scanline + // This is experimental code, not much tested, use with care. + CImg& load_magick(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_magick(): Specified filename is (null).", + cimg_instance); + +#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 *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned long off = (unsigned long)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + case Magick::PaletteType : + case Magick::TrueColorType : { + assign(W,H,1,3); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned long off = (unsigned long)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + ++pixels; + } + } break; + case Magick::GrayscaleMatteType : { + assign(W,H,1,2); + T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned long off = (unsigned long)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + default : { + assign(W,H,1,1); + T *ptr_r = data(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned long off = (unsigned long)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + ++pixels; + } + } + } +#else + throw CImgIOException(_cimg_instance + "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + return *this; + } + + //! Load image from a file, using Magick++ library \newinstance. + static CImg get_load_magick(const char *const filename) { + return CImg().load_magick(filename); + } + + //! Load image from a PNG file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_png(const char *const filename) { + return _load_png(0,filename); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(const char *const filename) { + return CImg().load_png(filename); + } + + //! Load image from a PNG file \overloading. + CImg& load_png(std::FILE *const file) { + return _load_png(file,0); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(std::FILE *const file) { + return CImg().load_png(file); + } + + // (Note: Most of this function has been written by Eric Fausett) + CImg& _load_png(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_png(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_png + if (file) + throw CImgIOException(_cimg_instance + "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", + cimg_instance); + + 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'. + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); + + unsigned char pngCheck[8] = { 0 }; + cimg::fread(pngCheck,8,(std::FILE*)nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Invalid PNG file '%s'.", + cimg_instance, + 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_instance + "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", + cimg_instance, + 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_instance + "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", + cimg_instance, + 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_instance + "load_png(): Failed to initialize 'end_info' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))) { + if (!file) cimg::fclose((std::FILE*)nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Encountered unknown fatal error in libpng for file '%s'.", + cimg_instance, + 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; + bool is_gray = false; + png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); + + // Transforms to unify image data + if (color_type==PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + color_type = PNG_COLOR_TYPE_RGB; + bit_depth = 8; + } + if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + is_gray = true; + bit_depth = 8; + } + if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + color_type |= PNG_COLOR_MASK_ALPHA; + } + if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + color_type |= PNG_COLOR_MASK_COLOR; + is_gray = true; + } + if (color_type==PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER); + + png_read_update_info(png_ptr,info_ptr); + if (bit_depth!=8 && bit_depth!=16) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Invalid bit depth %u in file '%s'.", + cimg_instance, + bit_depth,nfilename?nfilename:"(FILE*)"); + } + const int byte_depth = bit_depth>>3; + + // Allocate Memory for Image Read + png_bytep *const imgData = new png_bytep[H]; + for (unsigned int row = 0; row& load_pnm(const char *const filename) { + return _load_pnm(0,filename); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(const char *const filename) { + return CImg().load_pnm(filename); + } + + //! Load image from a PNM file \overloading. + CImg& load_pnm(std::FILE *const file) { + return _load_pnm(file,0); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(std::FILE *const file) { + return CImg().load_pnm(file); + } + + CImg& _load_pnm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pnm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type, W, H, D = 1, colormax = 255; + CImg item(16384,1,1,1,0); + int err, rval, gval, bval; + const long cimg_iobuffer = 12*1024*1024; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (std::sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (ppm_type!=1 && ppm_type!=4) { + if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (std::sscanf(item,"%u",&colormax)!=1) + cimg::warn(_cimg_instance + "load_pnm(): COLORMAX field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else { colormax = D; D = 1; } + } + std::fgetc(nfile); + + switch (ppm_type) { + case 1 : { // 2d b&w ascii. + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; } + } break; + case 2 : { // 2d grey ascii. + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; } + } break; + case 3 : { // 2d color ascii. + assign(W,H,1,3); + T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forXY(*this,x,y) { + if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; } + else break; + } + } break; + case 4 : { // 2d b&w binary (support 3D PINK extension). + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + unsigned int w = 0, h = 0, d = 0; + for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + unsigned char mask = 0, val = 0; + for (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) { + if (!mask) { if (off--) val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?0:255); + if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }} + } + } + } break; + case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension). + if (colormax<256) { // 8 bits. + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } else { // 16 bits. + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } + } break; + case 6 : { // 2d color binary. + if (colormax<256) { // 8 bits. + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { // 16 bits. + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (long to_read = (int)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } + } break; + case 8 : { // 2d/3d grey binary with int32 integers (PINK extension). + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const int *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + case 9 : { // 2d/3d grey binary with float values (PINK extension). + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const float *ptrs = raw._data; + for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + default : + assign(); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM type 'P%d' found, but type is not supported.", + cimg_instance, + filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PFM file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pfm(const char *const filename) { + return _load_pfm(0,filename); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(const char *const filename) { + return CImg().load_pfm(filename); + } + + //! Load image from a PFM file \overloading. + CImg& load_pfm(std::FILE *const file) { + return _load_pfm(file,0); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(std::FILE *const file) { + return CImg().load_pfm(file); + } + + CImg& _load_pfm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pfm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char pfm_type; + CImg item(16384,1,1,1,0); + int W = 0, H = 0, err = 0; + double scale = 0; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (std::sscanf(item," P%c",&pfm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): PFM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=std::sscanf(item," %d %d",&W,&H))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (std::sscanf(item,"%lf",&scale)!=1) + cimg::warn(_cimg_instance + "load_pfm(): SCALE field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + std::fgetc(nfile); + const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness(); + if (is_color) { + assign(W,H,1,3,0); + CImg buf(3*W); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forY(*this,y) { + cimg::fread(buf._data,3*W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,3*W); + const float *ptrs = buf._data; + cimg_forX(*this,x) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { + assign(W,H,1,1,0); + CImg buf(W); + T *ptrd = data(0,0,0,0); + cimg_forY(*this,y) { + cimg::fread(buf._data,W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,W); + const float *ptrs = buf._data; + cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return mirror('y'); // Most of the .pfm files are flipped along the y-axis. + } + + //! Load image from a RGB file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(0,filename,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(filename,dimw,dimh); + } + + //! Load image from a RGB file \overloading. + CImg& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(file,0,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(file,dimw,dimh); + } + + CImg& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgb(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const long cimg_iobuffer = 12*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (unsigned long off = raw._width/3UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a RGBA file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(0,filename,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(filename,dimw,dimh); + } + + //! Load image from a RGBA file \overloading. + CImg& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(file,0,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(file,dimw,dimh); + } + + CImg& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgba(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const long cimg_iobuffer = 12*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,4); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2), + *ptr_a = data(0,0,0,3); + for (long to_read = (long)size(); to_read>0; ) { + raw.assign(cimg::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (unsigned long off = raw._width/4UL; 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 image from a TIFF file. + /** + \param filename Filename, as a C-string. + \param first_frame First frame to read (for multi-pages tiff). + \param last_frame Last frame to read (for multi-pages tiff). + \param step_frame Step value of frame reading. + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tif is not defined at compilation time the + function uses CImg& load_other(const char*). + **/ + CImg& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Specified filename is (null).", + cimg_instance); + + const unsigned int + nfirst_frame = first_frame1) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", + cimg_instance, + 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_instance + "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).", + cimg_instance, + filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images-1; + TIFFSetDirectory(tif,0); + CImg frame; + for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { + frame._load_tiff(tif,l); + if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum); + if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) + resize(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0); + draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame); + } + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "load_tiff(): Failed to open file '%s'.", + cimg_instance, + filename); + return *this; +#endif + } + + //! Load image from a TIFF file \newinstance. + static CImg get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame); + } + + // (Original contribution by Jerome Boulanger). +#ifdef cimg_use_tiff + template + void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row + void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int vv = 0; vv + void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (row = 0; rowny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0; rr + void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (unsigned int vv = 0; vvny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0;rr& _load_tiff(TIFF *const tif, const unsigned int directory) { + if (!TIFFSetDirectory(tif,directory)) return assign(); + uint16 samplesperpixel = 1, bitspersample, photo; + uint16 sampleformat = SAMPLEFORMAT_UINT; + uint32 nx,ny; + const char *const filename = TIFFFileName(tif); + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); + TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + int spectrum = samplesperpixel; + if (photo == 3) spectrum = 3; + assign(nx,ny,1,spectrum); + if ((photo < 3) && ( bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4))) { + uint16 config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + if (TIFFIsTiled(tif)) { + uint32 tw, th; + TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); + TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : { + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + } break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + } + } else { + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + } else switch (bitspersample){ + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + } + } + } else { + uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); + if (!raster) { + _TIFFfree(raster); TIFFClose(tif); + throw CImgException(_cimg_instance + "load_tiff(): Failed to allocate memory (%s) for file '%s'.", + cimg_instance, + cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (spectrum) { + 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 image from a MINC2 file. + /** + \param filename Filename, as a C-string. + **/ + // (Original code by Haz-Edine Assemlal). + CImg& load_minc2(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_minc2(): Specified filename is (null).", + cimg_instance); +#ifndef cimg_use_minc2 + return load_other(filename); +#else + minc::minc_1_reader rdr; + rdr.open(filename); + assign(rdr.ndim(1)?rdr.ndim(1):1, + rdr.ndim(2)?rdr.ndim(2):1, + rdr.ndim(3)?rdr.ndim(3):1, + rdr.ndim(4)?rdr.ndim(4):1); + if(typeid(T)==typeid(unsigned char)) + rdr.setup_read_byte(); + else if(typeid(T)==typeid(int)) + rdr.setup_read_int(); + else if(typeid(T)==typeid(double)) + rdr.setup_read_double(); + else + rdr.setup_read_float(); + minc::load_standard_volume(rdr, this->_data); + return *this; +#endif + } + + //! Load image from a MINC2 file \newinstance. + static CImg get_load_minc2(const char *const filename) { + return CImg().load_analyze(filename); + } + + //! Load image from an ANALYZE7.5/NIFTI file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_analyze(const char *const filename, float *const voxel_size=0) { + return _load_analyze(0,filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(const char *const filename, float *const voxel_size=0) { + return CImg().load_analyze(filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \overloading. + CImg& load_analyze(std::FILE *const file, float *const voxel_size=0) { + return _load_analyze(file,0,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(std::FILE *const file, float *const voxel_size=0) { + return CImg().load_analyze(file,voxel_size); + } + + CImg& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_analyze(): Specified filename is (null).", + cimg_instance); + + std::FILE *nfile_header = 0, *nfile = 0; + if (!file) { + char body[1024] = { 0 }; + const char *const ext = cimg::split_filename(filename,body); + if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file. + nfile_header = cimg::fopen(filename,"rb"); + std::sprintf(body + std::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"); + std::sprintf(body + std::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_instance + "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", + cimg_instance, + 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_instance + "load_analyze(): Invalid zero-sized header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } + unsigned char *const 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_instance + "load_analyze(): File '%s' defines an image with zero dimensions.", + cimg_instance, + filename?filename:"(FILE*)"); + + if (dim[0]>4) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", + cimg_instance, + 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 (voxel_size) { + const float *vsize = (float*)(header+76); + voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3]; + } + delete[] header; + + // Read pixel data. + assign(dimx,dimy,dimz,dimv); + switch (datatype) { + case 2 : { + unsigned char *const 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 *const 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 *const 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 *const 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 *const 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_instance + "load_analyze(): Unable to load datatype %d in file '%s'", + cimg_instance, + datatype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a .cimg[z] file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(const char *const filename, const char axis='z', const float align=0) { + return CImg().load_cimg(filename,axis,align); + } + + //! Load image from a .cimg[z] file \overloading. + CImg& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + return CImg().load_cimg(file,axis,align); + } + + //! Load sub-images of a .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Starting frame. + \param n1 Ending frame. + \param x0 X-coordinate of the starting sub-image vertex. + \param y0 Y-coordinate of the starting sub-image vertex. + \param z0 Z-coordinate of the starting sub-image vertex. + \param c0 C-coordinate of the starting sub-image vertex. + \param x1 X-coordinate of the ending sub-image vertex. + \param y1 Y-coordinate of the ending sub-image vertex. + \param z1 Z-coordinate of the ending sub-image vertex. + \param c1 C-coordinate of the ending sub-image vertex. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load sub-images of a .cimg file \overloading. + CImg& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load image from an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_inr(const char *const filename, float *const voxel_size=0) { + return _load_inr(0,filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(const char *const filename, float *const voxel_size=0) { + return CImg().load_inr(filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \overloading. + CImg& load_inr(std::FILE *const file, float *const voxel_size=0) { + return _load_inr(file,0,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(std::FILE *const file, float *voxel_size=0) { + return CImg().load_inr(file,voxel_size); + } + + static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { + char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 }; + out[0] = 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(): INRIMAGE-4 header not found.", + pixel_type()); + + while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) { + std::sscanf(item," XDIM%*[^0-9]%d",out); + std::sscanf(item," YDIM%*[^0-9]%d",out+1); + std::sscanf(item," ZDIM%*[^0-9]%d",out+2); + std::sscanf(item," VDIM%*[^0-9]%d",out+3); + std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); + if (voxel_size) { + std::sscanf(item," VX%*[^0-9.+-]%f",voxel_size); + std::sscanf(item," VY%*[^0-9.+-]%f",voxel_size+1); + std::sscanf(item," VZ%*[^0-9.+-]%f",voxel_size+2); + } + if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { + case 0 : break; + case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1)-1); + 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<%s>::load_inr(): Invalid pixel type '%s' defined in header.", + pixel_type(), + tmp2); + } + } + if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.", + pixel_type(), + out[0],out[1],out[2],out[3]); + if(out[4]<0 || out[5]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.", + pixel_type()); + if(out[6]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.", + pixel_type()); + if(out[7]<0) + throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", + pixel_type()); + } + + CImg& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) { +#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *const 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_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_inr(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian=cimg::endianness()?1:0; + bool loaded = false; + if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1; + _load_inr_header(nfile,fopt,voxel_size); + 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_instance + "load_inr(): Unknown pixel type defined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a EXR file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_exr(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_exr(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_openexr + return load_other(filename); +#else + Imf::RgbaInputFile file(filename); + Imath::Box2i dw = file.dataWindow(); + const int + inwidth = dw.max.x - dw.min.x + 1, + inheight = dw.max.y - dw.min.y + 1; + Imf::Array2D pixels; + pixels.resizeErase(inheight,inwidth); + file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth); + file.readPixels(dw.min.y, dw.max.y); + assign(inwidth,inheight,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)pixels[y][x].r; + *(ptr_g++) = (T)pixels[y][x].g; + *(ptr_b++) = (T)pixels[y][x].b; + *(ptr_a++) = (T)pixels[y][x].a; + } + return *this; +#endif + } + + //! Load image from a EXR file \newinstance. + static CImg get_load_exr(const char *const filename) { + return CImg().load_exr(filename); + } + + //! Load image from a PANDORE-5 file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pandore(const char *const filename) { + return _load_pandore(0,filename); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(const char *const filename) { + return CImg().load_pandore(filename); + } + + //! Load image from a PANDORE-5 file \overloading. + CImg& load_pandore(std::FILE *const file) { + return _load_pandore(file,0); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(std::FILE *const file) { + return CImg().load_pandore(file); + } + + CImg& _load_pandore(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_instance \ + "load_pandore(): Unknown pixel datatype in file '%s'.", \ + cimg_instance, \ + filename?filename:"(FILE*)"); } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pandore(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char header[32] = { 0 }; + cimg::fread(header,12,nfile); + if (cimg::strncasecmp("PANDORE",header,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): PANDORE header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + unsigned int imageid, dims[8] = { 0 }; + 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,unsigned char,unsigned char,unsigned char,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,unsigned char,unsigned char,unsigned char,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,unsigned char,unsigned char,unsigned char,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,unsigned char,unsigned char,unsigned char,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,unsigned char,unsigned char,unsigned char,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],unsigned char,unsigned char,unsigned char,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],unsigned long,unsigned int,unsigned short,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],unsigned char,unsigned char,unsigned char,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],unsigned long,unsigned int,unsigned short,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],unsigned char,unsigned char,unsigned char,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],unsigned long,unsigned int,unsigned short,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] = { 0 }; + 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] = { 0 }; + 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] = { 0 }; + 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_instance + "load_pandore(): Unable to load data with ID_type %u in file '%s'.", + cimg_instance, + imageid,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PAR-REC (Philips) file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_parrec(const char *const filename, const char axis='c', const float align=0) { + CImgList list; + list.load_parrec(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a PAR-REC (Philips) file \newinstance. + static CImg get_load_parrec(const char *const filename, const char axis='c', const float align=0) { + return CImg().load_parrec(filename,axis,align); + } + + //! Load image from a raw binary file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the image buffer. + \param size_y Height of the image buffer. + \param size_z Depth of the image buffer. + \param size_c Spectrum of the image buffer. + \param is_multiplexed Tells if the image values are multiplexed along the C-axis. + \param invert_endianness Tells if the endianness of the image buffer must be inverted. + **/ + CImg& load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false) { + return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false) { + return CImg().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness); + } + + //! Load image from a raw binary file \overloading. + CImg& load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false) { + return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false) { + return CImg().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness); + } + + CImg& _load_raw(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const bool is_multiplexed, const bool invert_endianness) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename is (null).", + cimg_instance); + unsigned int siz = size_x*size_y*size_z*size_c, _size_x = size_x, _size_y = size_y, _size_z = size_z, _size_c = size_c; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!siz) { // Retrieve file size. + const long fpos = std::ftell(nfile); + if (fpos<0) throw CImgArgumentException(_cimg_instance + "load_raw(): Cannot determine size of input file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + std::fseek(nfile,0,SEEK_END); + siz = _size_y = (unsigned int)std::ftell(nfile)/sizeof(T); + _size_x = _size_z = _size_c = 1; + std::fseek(nfile,fpos,SEEK_SET); + } + assign(_size_x,_size_y,_size_z,_size_c,0); + if (!is_multiplexed || size_c==1) { + cimg::fread(_data,siz,nfile); + if (invert_endianness) cimg::invert_endianness(_data,siz); + } else { + CImg buf(1,1,1,_size_c); + cimg_forXYZ(*this,x,y,z) { + cimg::fread(buf._data,_size_c,nfile); + if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); + set_vector_at(buf,x,y,z); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image sequence using FFMPEG av's libraries. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param pixel_format To be documented. + \param resume To be documented. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, + const char axis='z', const float align=0) { + return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this); + } + + //! Load image sequence using FFMPEG av's libraries \newinstance. + static CImg get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, + const char axis='z', const float align=0) { + return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align); + } + + //! Load image sequence from a YUV file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the frames. + \param size_y Height of the frames. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param yuv2rgb Tells if the YUV to RGB transform must be applied. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + **/ + CImg& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load image sequence from a YUV file \overloading. + CImg& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load 3d object from a .OFF file. + /** + \param[out] primitives Primitives data of the 3d object. + \param[out] colors Colors data of the 3d object. + \param filename Filename, as a C-string. + **/ + template + CImg& load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return _load_off(primitives,colors,0,filename); + } + + //! Load 3d object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return CImg().load_off(primitives,colors,filename); + } + + //! Load 3d object from a .OFF file \overloading. + template + CImg& load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return _load_off(primitives,colors,file,0); + } + + //! Load 3d object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return CImg().load_off(primitives,colors,file); + } + + template + CImg& _load_off(CImgList& primitives, CImgList& colors, + std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_off(): Specified filename is (null).", + cimg_instance); + + 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 = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); + if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): OFF header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); + if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Invalid number of vertices or primitives specified in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read points data + assign(nb_points,3); + float X = 0, Y = 0, Z = 0; + cimg_forX(*this,l) { + do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); + if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Failed to read vertex %u/%u in file '%s'.", + cimg_instance, + l+1,nb_points,filename?filename:"(FILE*)"); + } + (*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; + if ((err = std::fscanf(nfile,"%u",&prim))!=1) stopflag=true; + else { + ++nb_read; + switch (prim) { + case 1 : { + if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 2 : { + if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 3 : { + if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 4 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 5 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 6 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 7 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i4,i3,i1).move_to(primitives); + CImg::vector(i0,i6,i5,i4).move_to(primitives); + CImg::vector(i3,i2,i1).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + case 8 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + CImg::vector(i0,i7,i6,i5).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + default : + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", + cimg_instance, + nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } + } + } + if (!file) cimg::fclose(nfile); + if (primitives._width!=nb_primitives) + cimg::warn(_cimg_instance + "load_off(): Only %u/%u primitives read from file '%s'.", + cimg_instance, + primitives._width,nb_primitives,filename?filename:"(FILE*)"); + return *this; + } + + //! Load image sequence using FFMPEG's external tool 'ffmpeg'. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) { + return get_load_ffmpeg_external(filename,axis,align).move_to(*this); + } + + //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance. + static CImg get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) { + return CImgList().load_ffmpeg_external(filename).get_append(axis,align); + } + + //! Load gif file, using Imagemagick or GraphicsMagicks's external tools. + /** + \param filename Filename, as a C-string. + \param use_graphicsmagick Tells if GraphicsMagick's tool 'gm' is used instead of ImageMagick's tool 'convert'. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return get_load_gif_external(filename,axis,align).move_to(*this); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance. + static CImg get_load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return CImgList().load_gif_external(filename).get_append(axis,align); + } + + //! Load image using GraphicsMagick's external tool 'gm'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_graphicsmagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-", + cimg::graphicsmagick_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"", + cimg::graphicsmagick_path(),s_filename.data(),CImg::string(filetmp)._system_strescape().data()); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filetmp); + std::remove(filetmp); + return *this; + } + + //! Load image using GraphicsMagick's external tool 'gm' \newinstance. + static CImg get_load_graphicsmagick_external(const char *const filename) { + return CImg().load_graphicsmagick_external(filename); + } + + //! Load gzipped image file, using external tool 'gunzip'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimg_instance + "load_gzip_external(): Specified filename is (null).", + cimg_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + const char + *const ext = cimg::split_filename(filename,body), + *const ext2 = cimg::split_filename(body,0); + + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); + cimg::system(command); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load(filetmp); + std::remove(filetmp); + return *this; + } + + //! Load gzipped image file, using external tool 'gunzip' \newinstance. + static CImg get_load_gzip_external(const char *const filename) { + return CImg().load_gzip_external(filename); + } + + //! Load image using ImageMagick's external tool 'convert'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_imagemagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_imagemagick_external(): Specified filename is (null).", + cimg_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,sizeof(command),"%s%s \"%s\" pnm:-", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with external command 'convert'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s%s \"%s\" \"%s\"", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(),CImg::string(filetmp)._system_strescape().data()); + cimg::system(command,cimg::imagemagick_path()); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with external command 'convert'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filetmp); + std::remove(filetmp); + return *this; + } + + //! Load image using ImageMagick's external tool 'convert' \newinstance. + static CImg get_load_imagemagick_external(const char *const filename) { + return CImg().load_imagemagick_external(filename); + } + + //! Load image from a DICOM file, using XMedcon's external tool 'medcon'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_medcon_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_medcon_external(): Specified filename is (null).", + cimg_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + cimg::fclose(cimg::fopen(filename,"r")); + std::FILE *file = 0; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand()); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filetmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + cimg::split_filename(filetmp,body); + + cimg_snprintf(command,sizeof(command),"%s.hdr",body); + file = std::fopen(command,"rb"); + if (!file) { + cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body); + file = std::fopen(command,"rb"); + if (!file) { + throw CImgIOException(_cimg_instance + "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + load_analyze(command); + std::remove(command); + cimg::split_filename(command,body); + cimg_snprintf(command,sizeof(command),"%s.img",body); + std::remove(command); + return *this; + } + + //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance. + static CImg get_load_medcon_external(const char *const filename) { + return CImg().load_medcon_external(filename); + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_dcraw_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_dcraw_external(): Specified filename is (null).", + cimg_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"", + cimg::dcraw_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > \"%s\"", + cimg::dcraw_path(),s_filename.data(),CImg::string(filetmp)._system_strescape().data()); + cimg::system(command,cimg::dcraw_path()); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filetmp); + std::remove(filetmp); + return *this; + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance. + static CImg get_load_dcraw_external(const char *const filename) { + return CImg().load_dcraw_external(filename); + } + + //! Load image from a camera stream, using OpenCV. + /** + \param camera_index Index of the camera to capture images from. + \param skip_frames Number of frames to skip before the capture. + \param release_camera Tells if the camera ressource must be released at the end of the method. + **/ + CImg& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, const bool release_camera=false, + const unsigned int capture_width=0, const unsigned int capture_height=0) { +#ifdef cimg_use_opencv + if (camera_index>255) + throw CImgArgumentException(_cimg_instance + "load_camera(): Invalid request for camera #%u (no more than 256 cameras can be managed).", + cimg_instance, + camera_index); + static CvCapture *capture[256] = { 0 }; + if (release_camera) { + if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index])); + capture[camera_index] = 0; + return *this; + } + if (!capture[camera_index]) { + capture[camera_index] = cvCreateCameraCapture(camera_index); + if (!capture[camera_index]) { + throw CImgIOException(_cimg_instance + "load_camera(): Failed to initialize camera #%u.", + cimg_instance, + camera_index); + } + } + if (capture_width) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width); + if (capture_height) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height); + const IplImage *img = 0; + for (unsigned int i = 0; iwidthStep - 3*img->width); + assign(img->width,img->height,1,3); + const unsigned char* ptrs = (unsigned char*)img->imageData; + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + if (step>0) cimg_forY(*this,y) { + cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); } + ptrs+=step; + } else for (unsigned long siz = (unsigned long)img->width*img->height; siz; --siz) { + *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); + } + } +#else + cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height); + throw CImgIOException(_cimg_instance + "load_camera(): This function requires the OpenCV library to run " + "(macro 'cimg_use_opencv' must be defined).", + cimg_instance); +#endif + return *this; + } + + //! Load image from a camera stream, using OpenCV \newinstance. + static CImg get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, const bool release_camera=false, + const unsigned int capture_width=0, const unsigned int capture_height=0) { + return CImg().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height); + } + + //! Load image using various non-native ways. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_other(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_other(): Specified filename is (null).", + cimg_instance); + + const unsigned int omode = 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&) { + try { load_cimg(filename); } + catch (CImgException&) { + try { + std::fclose(cimg::fopen(filename,"rb")); + } catch (CImgException&) { + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load_other(): Failed to open file '%s'.", + cimg_instance, + filename); + } + cimg::exception_mode() = omode; + throw CImgIOException(_cimg_instance + "load_other(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + } + } + cimg::exception_mode() = omode; + return *this; + } + + //! Load image using various non-native ways \newinstance. + static CImg get_load_other(const char *const filename) { + return CImg().load_other(filename); + } + + //@} + //--------------------------- + // + //! \name Data Output + //@{ + //--------------------------- + + //! Display informations about the image data. + /** + \param title Name for the considered image. + \param display_stats Tells to compute and display image statistics. + **/ + const CImg& print(const char *const title=0, const bool display_stats=true) const { + int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; + static CImg st; + if (!is_empty() && display_stats) { + st = get_stats(); + xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; + xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; + } + const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1, mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1; + + char _title[64] = { 0 }; + if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type()); + + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared"); + else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); + + if (!is_empty()) cimg_foroff(*this,off) { + std::fprintf(cimg::output(),cimg::type::format(),cimg::type::format(_data[off])); + if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); + if (off==7 && siz>16) { off = siz1-8; std::fprintf(cimg::output(),"... "); } + } + if (!is_empty() && display_stats) + std::fprintf(cimg::output()," ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), %scoords_max%s = (%u,%u,%u,%u).\n", + cimg::t_bold,cimg::t_normal,st[0], + cimg::t_bold,cimg::t_normal,st[1], + cimg::t_bold,cimg::t_normal,st[2], + cimg::t_bold,cimg::t_normal,std::sqrt(st[3]), + cimg::t_bold,cimg::t_normal,xm,ym,zm,vm, + cimg::t_bold,cimg::t_normal,xM,yM,zM,vM); + else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" "); + std::fflush(cimg::output()); + return *this; + } + + //! Display image into a CImgDisplay window. + /** + \param disp Display window. + **/ + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display image into a CImgDisplay window, in an interactive way. + /** + \param disp Display window. + \param display_info Tells if image informations are displayed on the standard output. + **/ + const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const { + return _display(disp,0,display_info,XYZ,false); + } + + //! Display image into an interactive window. + /** + \param title Window title + \param display_info Tells if image informations are displayed on the standard output. + **/ + const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const { + CImgDisplay disp; + return _display(disp,title,display_info,XYZ,false); + } + + const CImg& _display(CImgDisplay &disp, const char *const title, + const bool display_info, unsigned int *const XYZ, + const bool exit_on_simpleclick) const { + unsigned int oldw = 0, oldh = 0, _XYZ[3], key = 0; + int x0 = 0, y0 = 0, z0 = 0, x1 = width()-1, y1 = height()-1, z1 = depth()-1; + + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + else disp.set_title("%s",title); + } else if (title) disp.set_title("%s",title); + disp.show().flush(); + + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(dtitle); + + CImg zoom; + for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { + if (reset_view) { + if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; } + else { _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==width()-1 && y1==height()-1 && z1==depth()-1) { if (is_empty()) zoom.assign(1,1,1,1,0); else 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 (!is_empty() && !disp.is_fullscreen() && resize_disp) { + const unsigned int + ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh, + dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()), + imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM); + disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); + resize_disp = false; + } + oldw = tw; oldh = th; + + bool + go_up = false, go_down = false, go_left = false, go_right = false, + go_inc = false, go_dec = false, go_in = false, go_out = false, + go_in_center = false; + const CImg& visu = zoom?zoom:*this; + + disp.set_title(dtitle._data); + if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0); + if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); + if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); + + const CImg selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,is_first_select,_depth>1); + is_first_select = false; + + if (disp.wheel()) { + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; } + else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); } + else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); } + disp.set_wheel(); + } + + 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) { + if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true; + } + resize_disp = true; + } else switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT : +#if cimg_OS!=2 + case cimg::keyALTGR : +#endif + case cimg::keyALT : key = 0; break; + case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // 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)); + float frame_timing = 5; + bool is_stopped = false; + disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0; + for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) { + if (disp.is_resized()) disp.resize(false); + if (!timer) { + visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2])); + (++_XYZ[2])%=visu._depth; + } + if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U; + if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); } + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break; + case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break; + case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; (_XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).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.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen().set_key(key,false); key = 0; + } break; + } + frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing); + 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(dtitle.data()).set_key().set_button().set_wheel(); + key = 0; + } break; + case cimg::keyHOME : 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.width()/2:disp.mouse_x(), + my = go_in_center?disp.height()/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=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = _XYZ[1]; } + if (mX>=width() && mY4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; } + if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; } + if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; } + } + if (go_out) { + const int + delta_x = (x1-x0)/8, delta_y = (y1-y0)/8, delta_z = (z1-z0)/8, + ndelta_x = delta_x?delta_x:(_width>1?1:0), + ndelta_y = delta_y?delta_y:(_height>1?1:0), + ndelta_z = delta_z?delta_z:(_depth>1?1:0); + x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z; + x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; } + if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; } + if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; } + if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; } + if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; } + if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; } + } + if (go_left) { + const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0); + if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + } + if (go_right) { + const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0); + if (x1+ndelta1?1:0); + if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; } + else { y1-=y0; y0 = 0; } + } + if (go_down) { + const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0); + if (y1+ndelta1?1:0); + if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; } + else { z1-=z0; z0 = 0; } + } + if (go_dec) { + const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0); + if (z1+ndelta + const CImg& display_object3d(CImgDisplay& disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgDisplay disp; + return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + //! Display object 3d in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5000, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + + template + const CImg& _display_object3d(CImgDisplay& disp, const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering, + const int render_static, const int render_motion, + const bool is_double_sided, const float focale, + const float light_x, const float light_y, const float light_z, + const float specular_lightness, const float specular_shininess, + const bool display_axes, float *const pose_matrix) const { + typedef typename cimg::superset::type tpfloat; + + // Check input arguments + if (is_empty()) { + if (disp) return CImg(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0). + _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + else return CImg(1,2,1,1,64,128).resize(cimg_fitscreen(640,480,1),1,(colors && colors[0].size()==1)?1:3,3). + _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } else { if (disp) disp.resize(*this,false); } + char error_message[1024] = { 0 }; + if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgArgumentException(_cimg_instance + "display_object3d(): Invalid specified 3d object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message); + if (vertices._width && !primitives) { + CImgList nprimitives(vertices._width,1,1,1,1); + cimglist_for(nprimitives,l) nprimitives(l,0) = l; + return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix); + } + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); + if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",pixel_type(),vertices._width,primitives._width); + } else if (title) disp.set_title("%s",title); + + // Init 3d objects and compute object statistics + CImg + pose, + rotated_vertices(vertices._width,3), + bbox_vertices, rotated_bbox_vertices, + axes_vertices, rotated_axes_vertices, + bbox_opacities, axes_opacities; + CImgList bbox_primitives, axes_primitives; + CImgList reverse_primitives; + CImgList bbox_colors, bbox_colors2, axes_colors; + unsigned int ns_width = 0, ns_height = 0; + int _is_double_sided = (int)is_double_sided; + bool ndisplay_axes = display_axes; + const CImg + background_color(1,1,1,_spectrum,0), + foreground_color(1,1,1,_spectrum,255); + float + Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1, + xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0, + ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0, + zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0; + const float delta = cimg::max(xM-xm,yM-ym,zM-zm); + + rotated_bbox_vertices = bbox_vertices.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,_spectrum,1,1,1,background_color[0]); + bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]); + bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f); + + rotated_axes_vertices = axes_vertices.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,_spectrum,1,1,1,foreground_color[0]); + axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); + + // Begin user interaction loop + CImg visu0(*this), visu; + CImg zbuffer(visu0.width(),visu0.height(),1,1,0); + bool init_pose = true, clicked = false, redraw = true; + unsigned int key = 0; + int + x0 = 0, y0 = 0, x1 = 0, y1 = 0, + nrender_static = render_static, + nrender_motion = render_motion; + disp.show().flush(); + + while (!disp.is_closed() && !key) { + + // Init object pose + if (init_pose) { + const float + ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1, + dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; + if (centering) + CImg(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); + else CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose); + if (pose_matrix) { + CImg pose0(pose_matrix,4,3,1,1,false); + pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0); + pose0(3,3) = pose(3,3) = 1; + (pose0*pose).get_crop(0,0,3,2).move_to(pose); + Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15]; + } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; } + init_pose = 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 && nrender_motion>=0) || (!clicked && nrender_static>=0)) + cimg_forX(vertices,l) { + const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2); + rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30; + rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31; + rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32; + } + else cimg_forX(bbox_vertices,l) { + const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2); + rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; + } + + // Draw object + visu = visu0; + if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0)) + visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). + draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); + else visu._draw_object3d((void*)0,(!clicked && nrender_static>0)?zbuffer.fill(0):CImg::empty(), + Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, + width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_lightness,specular_shininess, + sprite_scale); + // Draw axes + if (ndisplay_axes) { + const float + n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02), + _r00 = r00/n, _r10 = r10/n, _r20 = r20/n, + _r01 = r01/n, _r11 = r11/n, _r21 = r21/n, + _r02 = r01/n, _r12 = r12/n, _r22 = r22/n, + Xaxes = 25, Yaxes = visu._height - 38.0f; + cimg_forX(axes_vertices,l) { + const float + x = axes_vertices(l,0), + y = axes_vertices(l,1), + z = axes_vertices(l,2); + rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z; + rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z; + rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z; + } + axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f; + axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f; + axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f; + visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,axes_colors,axes_opacities,1,false,focale). + draw_text((int)(Xaxes+rotated_axes_vertices(4,0)), + (int)(Yaxes+rotated_axes_vertices(4,1)), + "X",axes_colors[0]._data,0,axes_opacities(0,0),13). + draw_text((int)(Xaxes+rotated_axes_vertices(5,0)), + (int)(Yaxes+rotated_axes_vertices(5,1)), + "Y",axes_colors[1]._data,0,axes_opacities(1,0),13). + draw_text((int)(Xaxes+rotated_axes_vertices(6,0)), + (int)(Yaxes+rotated_axes_vertices(6,1)), + "Z",axes_colors[2]._data,0,axes_opacities(2,0),13); + } + visu.display(disp); + if (!clicked || nrender_motion==nrender_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.width()/2), + v0 = (float)(y0-disp.height()/2), + u1 = (float)(x1-disp.width()/2), + v1 = (float)(y1-disp.height()/2), + n0 = (float)std::sqrt(u0*u0+v0*v0), + n1 = (float)std::sqrt(u1*u1+v1*v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)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)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)std::sqrt(u*u+v*v+w*w), + alpha = (float)std::asin(n/R2); + (CImg::rotation_matrix(u,v,w,alpha)*pose).move_to(pose); + x0 = x1; y0 = y1; + } + if (disp.button()&2) { + if (focale>0) Zoff-=(y0-y1)*focale/400; + else { const float s = std::exp((y0-y1)/400.0f); pose*=s; sprite_scale*=s; } + x0 = x1; y0 = y1; + } + if (disp.wheel()) { + if (focale>0) Zoff-=disp.wheel()*focale/20; + else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; } + disp.set_wheel(); + } + if (disp.button()&4) { Xoff+=(x1-x0); Yoff+=(y1-y0); x0 = x1; y0 = y1; } + if ((disp.button()&1) && (disp.button()&2)) { + init_pose = true; disp.set_button(); x0 = x1; y0 = y1; + pose = CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0); + } + } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).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.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + if (!ns_width || !ns_height || + ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) { + ns_width = disp.screen_width()*3U/4; + ns_height = disp.screen_height()*3U/4; + } + if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); + else { + ns_width = (unsigned int)disp.width(); ns_height = disp.height(); + disp.resize(disp.screen_width(),disp.screen_height(),false); + } + disp.toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Switch single/double-sided primitives. + if (--_is_double_sided==-2) _is_double_sided = 1; + if (_is_double_sided>=0) reverse_primitives.assign(); + else primitives.get_reverse_object3d().move_to(reverse_primitives); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer + if (zbuffer) zbuffer.assign(); + else zbuffer.assign(visu0.width(),visu0.height(),1,1,0); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes. + ndisplay_axes = !ndisplay_axes; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points. + nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines. + nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat. + nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded. + nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to gouraud-shaded. + nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded. + nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + visu.save(filename); + visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp); + vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); + visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp); + vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save(filename); + visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + disp.set_key(key,false); key = 0; + } break; +#ifdef cimg_use_board + case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_lightness,specular_shininess, + sprite_scale); + board.saveEPS(filename); + visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_lightness,specular_shininess, + sprite_scale); + board.saveSVG(filename); + visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + disp.set_key(key,false); 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) { + std::memcpy(pose_matrix,pose._data,12*sizeof(float)); + pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale; + } + disp.set_button().set_key(key); + return *this; + } + + //! Display 1d graph in an interactive window. + /** + \param disp Display window. + \param plot_type Plot type. Can be { 0=points | 1=segments | 2=splines | 3=bars }. + \param vertex_type Vertex type. + \param labelx Title for the horizontal axis, as a C-string. + \param xmin Minimum value along the X-axis. + \param xmax Maximum value along the X-axis. + \param labely Title for the vertical axis, as a C-string. + \param ymin Minimum value along the X-axis. + \param ymax Maximum value along the X-axis. + **/ + const CImg& display_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "display_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type()); + const unsigned long siz = (unsigned long)_width*_height*_depth, siz1 = cimg::max(1U,siz-1); + const unsigned int old_normalization = disp.normalization(); + disp.show().flush()._normalization = 0; + + double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; + if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } + int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; + + for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) { + if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; } + CImg zoom(x1-x0+1,1,1,spectrum()); + cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(data(x0,0,0,c),x1-x0+1,1,1,1,true); + + if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; } + if (y0==y1) { --y0; ++y1; } + const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, + labelx, + nxmin + x0*(nxmax-nxmin)/siz1, + nxmin + x1*(nxmax-nxmin)/siz1, + labely,y0,y1); + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + if (selection[0]>=0) { + if (selection[2]<0) reset_view = true; + else { + x1 = x0 + selection[2]; x0+=selection[0]; + if (selection[1]>=0 && selection[3]>=0) { + y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32); + y1-=selection[1]*(y1-y0)/(disp.height()-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 : reset_view = resize_disp = true; key = 0; disp.set_key(); break; + case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break; + case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break; + case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); break; + case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break; + case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break; + case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break; + case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; + } + if (disp.wheel()) { + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0); + else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); + else go_up = !(go_down = disp.wheel()<0); + key = 0; + } + + if (go_in) { + const int + xsiz = x1 - x0, + mx = (mouse_x-16)*xsiz/(disp.width()-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() || disp.is_keyCTRLRIGHT()) { + const double + ysiz = y1 - y0, + my = (mouse_y-16)*ysiz/(disp.height()-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) { + if (x0>0 || x1<(int)siz1) { + const int delta_x = (x1-x0)/8, ndelta_x = delta_x?delta_x:(siz>1?1:0); + const double ndelta_y = (y1-y0)/8; + x0-=ndelta_x; x1+=ndelta_x; + y0-=ndelta_y; y1+=ndelta_y; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; } + if (x1>=(int)siz) { x0-=(x1-siz1); x1 = (int)siz1; if (x0<0) x0 = 0; } + } + } + 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+=(siz1-x1); x1 = siz1; } + 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 = old_normalization; + return *this; + } + + //! Display 1d graph in an interactive window \overloading. + const CImg& display_graph(const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "display_graph(): Empty instance.", + cimg_instance); + CImgDisplay disp; + return display_graph(disp.set_title("%s",title),plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); + } + + //! Save image as a file. + /** + \param filename Filename, as a C-string. + \param number When positive, represents an index added to the filename. + \note + - The used file format is defined by the file extension in the filename \p filename. + - Parameter \p number can be used to add a 6-digit number to the filename before saving. + **/ + const CImg& save(const char *const filename, const int number=-1) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save(): Specified filename is (null).", + cimg_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const char *const ext = cimg::split_filename(filename); + char nfilename[1024] = { 0 }; + 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); + else if (!cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); + else if (!cimg::strcasecmp(ext,"cpp") || + !cimg::strcasecmp(ext,"hpp") || + !cimg::strcasecmp(ext,"h") || + !cimg::strcasecmp(ext,"c")) return save_cpp(fn); + + // 2d binary formats + else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); + else 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); + else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); + else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); + else if (!cimg::strcasecmp(ext,"png")) return save_png(fn); + else if (!cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); + else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn); + else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn); + else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); + + // 3d binary formats + else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); + else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); + else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn); + else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); + else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + + // Image sequences + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + else 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,"ogg") || + !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 image as an ascii file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save image as an ascii file \overloading. + const CImg& save_ascii(std::FILE *const file) const { + return _save_ascii(file,0); + } + + const CImg& _save_ascii(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_ascii(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.16g ",(double)*(ptrs++)); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a .cpp source file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); + } + + //! Save image as a .cpp source file \overloading. + const CImg& save_cpp(std::FILE *const file) const { + return _save_cpp(file,0); + } + + const CImg& _save_cpp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_cpp(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + char varname[1024] = { 0 }; + if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname); + if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed"); + std::fprintf(nfile, + "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" + "%s data_%s[] = { %s\n ", + varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname, + is_empty()?"};":""); + if (!is_empty()) for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) { + std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); + if (off==siz) std::fprintf(nfile," };\n"); + else if (!((off+1)%16)) std::fprintf(nfile,",\n "); + else std::fprintf(nfile,", "); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a DLM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); + } + + //! Save image as a DLM file \overloading. + const CImg& save_dlm(std::FILE *const file) const { + return _save_dlm(file,0); + } + + const CImg& _save_dlm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_dlm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.16g%s",(double)*(ptrs++),(x==width()-1)?"":","); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a BMP file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); + } + + //! Save image as a BMP file \overloading. + const CImg& save_bmp(std::FILE *const file) const { + return _save_bmp(file,0); + } + + const CImg& _save_bmp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_bmp(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + 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)*height(), + 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 + *ptr_r = data(0,_height-1,0,0), + *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0; + + switch (_spectrum) { + case 1 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + const unsigned char val = (unsigned char)*(ptr_r++); + std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; + } + } break; + case 2 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc(0,nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; + } + } break; + default : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc((unsigned char)(*(ptr_b++)),nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a JPEG file. + /** + \param filename Filename, as a C-string. + \param quality Image quality (in %) + **/ + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); + } + + //! Save image as a JPEG file \overloading. + const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); + } + + const CImg& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_jpeg(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException(_cimg_instance + "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.", + cimg_instance); +#else + unsigned int dimbuf = 0; + J_COLOR_SPACE colortype = JCS_RGB; + + switch(_spectrum) { + case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; + case 2 : dimbuf = 3; colortype = JCS_RGB; break; + case 3 : dimbuf = 3; colortype = JCS_RGB; break; + default : dimbuf = 4; colortype = JCS_CMYK; break; + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + 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); + + JSAMPROW row_pointer[1]; + CImg buffer((unsigned long)_width*dimbuf); + + while (cinfo.next_scanline < cinfo.image_height) { + unsigned char *ptrd = buffer._data; + + // Fill pixel buffer + switch (_spectrum) { + case 1 : { // Greyscale images + const T *ptr_g = data(0, cinfo.next_scanline); + for(unsigned int b = 0; b < cinfo.image_width; b++) + *(ptrd++) = (unsigned char)*(ptr_g++); + } break; + case 2 : { // RG images + const T *ptr_r = data(0,cinfo.next_scanline,0,0), + *ptr_g = data(0,cinfo.next_scanline,0,1); + for(unsigned int b = 0; b < cinfo.image_width; ++b) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = 0; + } + } break; + case 3 : { // RGB images + const T *ptr_r = data(0,cinfo.next_scanline,0,0), + *ptr_g = data(0,cinfo.next_scanline,0,1), + *ptr_b = data(0,cinfo.next_scanline,0,2); + for(unsigned int b = 0; b < cinfo.image_width; ++b) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + } + } break; + default : { // CMYK images + const T *ptr_r = data(0,cinfo.next_scanline,0,0), + *ptr_g = data(0,cinfo.next_scanline,0,1), + *ptr_b = data(0,cinfo.next_scanline,0,2), + *ptr_a = data(0,cinfo.next_scanline,0,3); + for(unsigned int b = 0; b < cinfo.image_width; ++b) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + *(ptrd++) = (unsigned char)*(ptr_a++); + } + } + } + *row_pointer = buffer._data; + jpeg_write_scanlines(&cinfo,row_pointer,1); + } + jpeg_finish_compress(&cinfo); + if (!file) cimg::fclose(nfile); + jpeg_destroy_compress(&cinfo); + return *this; +#endif + } + + //! Save image, using built-in ImageMagick++ library. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible. + **/ + const CImg& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_magick(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_magick + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_magick(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + Magick::Image image(Magick::Geometry(_width,_height),"black"); + image.type(Magick::TrueColorType); + image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8)); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = _spectrum>1?data(0,0,0,1):0, + *ptr_b = _spectrum>2?data(0,0,0,2):0; + Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); + switch (_spectrum) { + case 1 : // Scalar images + for (unsigned long off = (unsigned long)_width*_height; off; --off) { + pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++); + ++pixels; + } + break; + case 2 : // RG images + for (unsigned long off = (unsigned long)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = 0; ++pixels; + } + break; + default : // RGB images + for (unsigned long off = (unsigned long)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = (Magick::Quantum)*(ptr_b++); + ++pixels; + } + } + image.syncPixels(); + image.write(filename); +#else + cimg::unused(bytes_per_pixel); + throw CImgIOException(_cimg_instance + "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + return *this; + } + + //! Save image as a PNG file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible. + **/ + const CImg& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_png(0,filename,bytes_per_pixel); + } + + //! Save image as a PNG file \overloading. + const CImg& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_png(file,0,bytes_per_pixel); + } + + const CImg& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_png(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + +#ifndef cimg_use_png + cimg::unused(bytes_per_pixel); + if (!file) return save_other(filename); + else throw CImgIOException(_cimg_instance + "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", + cimg_instance); +#else + const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); + + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>4) + cimg::warn(_cimg_instance + "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + // 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_instance + "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", + cimg_instance, + 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_instance + "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", + cimg_instance, + 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_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8); + int color_type; + switch (spectrum()) { + 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 = spectrum()>4?4:spectrum(); + const int pixel_bit_depth_flag = numChan * (bit_depth-1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *const 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 = data(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 = data(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 = data(0,0,0,1), *pC2 = data(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 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(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 = data(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 = data(0,0,0,1), *pC2 = data(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 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(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_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + 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 image as a PNM file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving. + **/ + const CImg& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(0,filename,bytes_per_pixel); + } + + //! Save image as a PNM file \overloading. + const CImg& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(file,0,bytes_per_pixel); + } + + const CImg& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + stmin,stmax,filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const unsigned long buf_size = cimg::min(1024*1024UL,_width*_height*(_spectrum==1?1UL:3UL)); + + std::fprintf(nfile,"P%c\n%u %u\n%u\n", + (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535)); + + switch (_spectrum) { + case 1 : { // Scalar image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Binary PGM 16 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + unsigned short *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + } break; + case 2 : { // RG image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = 0; + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } break; + default : { // RGB image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = (unsigned short)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PNK file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pnk(const char *const filename) const { + return _save_pnk(0,filename); + } + + //! Save image as a PNK file \overloading. + const CImg& save_pnk(std::FILE *const file) const { + return _save_pnk(file,0); + } + + const CImg& _save_pnk(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnk(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T *ptr = data(0,0,0,0); + + if (!cimg::type::is_float() && sizeof(T)==1 && _depth<2) _save_pnm(file,filename,0); // Can be saved as regular PNM file. + else if (!cimg::type::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d. + std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth); + CImg buf(buf_size); + for (long to_write = (long)_width*_height*_depth; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else if (!cimg::type::is_float()) { // Save as P8: Binary int32-valued 3d. + if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max()); + else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max()); + CImg buf(buf_size); + for (long to_write = (long)_width*_height*_depth; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + int *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (int)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Save as P9: Binary float-valued 3d. + if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max()); + else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max()); + CImg buf(buf_size); + for (long to_write = (long)_width*_height*_depth; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + float *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PFM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pfm(const char *const filename) const { + return get_mirror('y')._save_pfm(0,filename); + } + + //! Save image as a PFM file \overloading. + const CImg& save_pfm(std::FILE *const file) const { + return get_mirror('y')._save_pfm(file,0); + } + + const CImg& _save_pfm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pfm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pfm(): image instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + + std::fprintf(nfile,"P%c\n%u %u\n1.0\n", + (_spectrum==1?'f':'F'),_width,_height); + + switch (_spectrum) { + case 1 : { // Scalar image + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + float *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } break; + case 2 : { // RG image + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } break; + default : { // RGB image + CImg buf(buf_size); + for (long to_write = (long)_width*_height; to_write>0; ) { + const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (unsigned long i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = (float)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a RGB file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save image as a RGB file \overloading. + const CImg& save_rgb(std::FILE *const file) const { + return _save_rgb(file,0); + } + + const CImg& _save_rgb(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgb(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=3) + cimg::warn(_cimg_instance + "save_rgb(): image instance has not exactly 3 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned long wh = (unsigned long)_width*_height; + unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0; + switch (_spectrum) { + case 1 : { // Scalar image + for (unsigned long k = 0; k& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); + } + + //! Save image as a RGBA file \overloading. + const CImg& save_rgba(std::FILE *const file) const { + return _save_rgba(file,0); + } + + const CImg& _save_rgba(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgba(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=4) + cimg::warn(_cimg_instance + "save_rgba(): image instance has not exactly 4 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned long wh = (unsigned long)_width*_height; + unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0, + *ptr4 = _spectrum>3?data(0,0,0,3):0; + switch (_spectrum) { + case 1 : { // Scalar images + for (unsigned long k = 0; k{ 1=None | 2=CCITTRLE | 3=CCITTFAX3 | 4=CCITTFAX4 | 5=LZW | 6=JPEG }. + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tif is not defined at compilation time the + function uses CImg&save_other(const char*). + **/ + const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_tiff(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_tiff + TIFF *tif = TIFFOpen(filename,"w"); + if (tif) { + cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression_type); + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "save_tiff(): Failed to open file '%s' for writing.", + cimg_instance, + filename); +#else + cimg::unused(compression_type); + return save_other(filename); +#endif + return *this; + } + +#ifdef cimg_use_tiff + +#define _cimg_save_tiff(types,typed,compression_type) \ + if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression_type); } + + // [internal] Save a plane into a tiff file + template + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, const unsigned int compression_type) const { + if (is_empty() || !tif || pixel_t) return *this; + const char *const filename = TIFFFileName(tif); + uint32 rowsperstrip = (uint32)-1; + uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; + if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; + else photometric = PHOTOMETRIC_MINISBLACK; + TIFFSetDirectory(tif,directory); + TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width); + TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height); + TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); + TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); + if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); + else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); + else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); + TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); + TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type?(compression_type-1):COMPRESSION_NONE); + rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); + TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); + TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + t *const 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& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression_type) const { + _cimg_save_tiff("bool",unsigned char,compression_type); + _cimg_save_tiff("char",char,compression_type); + _cimg_save_tiff("unsigned char",unsigned char,compression_type); + _cimg_save_tiff("short",short,compression_type); + _cimg_save_tiff("unsigned short",unsigned short,compression_type); + _cimg_save_tiff("int",int,compression_type); + _cimg_save_tiff("unsigned int",unsigned int,compression_type); + _cimg_save_tiff("long",int,compression_type); + _cimg_save_tiff("unsigned long",unsigned int,compression_type); + _cimg_save_tiff("float",float,compression_type); + _cimg_save_tiff("double",float,compression_type); + const char *const filename = TIFFFileName(tif); + throw CImgInstanceException(_cimg_instance + "save_tiff(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + return *this; + } +#endif + + //! Save image as a MINC2 file. + /** + \param filename Filename, as a C-string. + \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from. + **/ + const CImg& save_minc2(const char *const filename, + const char *const imitate_file=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_minc2(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_minc2 + cimg::unused(imitate_file); + return save_other(filename); +#else + minc::minc_1_writer wtr; + if (imitate_file) + wtr.open(filename, imitate_file); + else { + minc::minc_info di; + if(width()) di.push_back(minc::dim_info(width(), width()*0.5, -1, minc::dim_info::DIM_X)); + if(height()) di.push_back(minc::dim_info(height(), height()*0.5, -1, minc::dim_info::DIM_Y)); + if(depth()) di.push_back(minc::dim_info(depth(), depth()*0.5, -1, minc::dim_info::DIM_Z)); + if(spectrum()) di.push_back(minc::dim_info(spectrum(), spectrum()*0.5, -1, minc::dim_info::DIM_TIME)); + wtr.open(filename, di, 1, NC_FLOAT, 0); + } + if(typeid(T)==typeid(unsigned char)) + wtr.setup_write_byte(); + else if(typeid(T)==typeid(int)) + wtr.setup_write_int(); + else if(typeid(T)==typeid(double)) + wtr.setup_write_double(); + else + wtr.setup_write_float(); + minc::save_standard_volume(wtr, this->_data); + return *this; +#endif + } + + //! Save image as an ANALYZE7.5 or NIFTI file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_analyze(const char *const filename, const float *const voxel_size=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_analyze(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + std::FILE *file; + char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 }; + const char *const ext = cimg::split_filename(filename); + short datatype=-1; + std::memset(header,0,348); + if (!*ext) { cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename); cimg_snprintf(iname,sizeof(iname),"%s.img",filename); } + if (!cimg::strncasecmp(ext,"hdr",3)) { + std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(iname + std::strlen(iname)-3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(hname + std::strlen(iname)-3,"hdr"); + } + if (!cimg::strncasecmp(ext,"nii",3)) { + std::strncpy(hname,filename,sizeof(hname)-1); *iname = 0; + } + int *const iheader = (int*)header; + *iheader = 348; + std::strcpy(header + 4,"CImg"); + std::strcpy(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] = _spectrum; + 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_instance + "save_analyze(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename); + + ((short*)(header+70))[0] = datatype; + ((short*)(header+72))[0] = sizeof(T); + ((float*)(header+112))[0] = 1; + ((float*)(header+76))[0] = 0; + if (voxel_size) { + ((float*)(header+76))[1] = voxel_size[0]; + ((float*)(header+76))[2] = voxel_size[1]; + ((float*)(header+76))[3] = voxel_size[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) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } + cimg::fwrite(_data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param is_compressed Tells if the file contains compressed image data. + **/ + const CImg& save_cimg(const char *const filename, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(filename,is_compressed); + return *this; + } + + //! Save image as a .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(file,is_compressed); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Index of the image inside the file. + \param x0 X-coordinate of the sub-image location. + \param y0 Y-coordinate of the sub-image location. + \param z0 Z-coordinate of the sub-image location. + \param c0 C-coordinate of the sub-image location. + **/ + const CImg& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,c0); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,c0); + return *this; + } + + //! Save blank image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param dx Width of the image. + \param dy Height of the image. + \param dz Depth of the image. + \param dc Number of channels of the image. + \note + - All pixel values of the saved image are set to \c 0. + - Use this method to save large images without having to instanciate and allocate them. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dc); + } + + //! Save blank image as a .cimg file \overloading. + /** + Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int) + with a file stream argument instead of a filename string. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(file,1,dx,dy,dz,dc); + } + + //! Save image as an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_inr(const char *const filename, const float *const voxel_size=0) const { + return _save_inr(0,filename,voxel_size); + } + + //! Save image as an INRIMAGE-4 file \overloading. + const CImg& save_inr(std::FILE *const file, const float *const voxel_size=0) const { + return _save_inr(file,0,voxel_size); + } + + const CImg& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_inr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + 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_instance + "save_inr(): Unsupported pixel type '%s' for file '%s'", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + char header[257] = { 0 }; + int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum); + if (voxel_size) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[2]); + err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + std::memset(header + err,'\n',252 - err); + std::memcpy(header + 252,"##}\n",4); + cimg::fwrite(header,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as an OpenEXR file. + /** + \param filename Filename, as a C-string. + \note The OpenEXR file format is described here. + **/ + const CImg& save_exr(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_exr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + +#ifndef cimg_use_openexr + return save_other(filename); +#else + Imf::Rgba *const ptrd0 = new Imf::Rgba[(unsigned long)_width*_height], *ptrd = ptrd0, rgba; + switch (_spectrum) { + case 1 : { // Grayscale image. + for (const T *ptr_r = data(), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_rPandore file specifications for more informations). + **/ + const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { + return _save_pandore(0,filename,colorspace); + } + + //! Save image as a Pandore-5 file \overloading. + /** + Same as save_pandore(const char *,unsigned int) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { + return _save_pandore(file,0,colorspace); + } + + 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] = _spectrum; 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] = _spectrum; dims[1] = _width; nbdims = 2; } + if (id==26 || id==27 || id==29) { dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; } + if (id==30 || id==31 || id==33) { dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; } + return nbdims; + } + + const CImg& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const { + +#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==_spectrum):true) && !std::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(unsigned long)==4) { unsigned long ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + __cimg_save_pandore_case(unsigned char); \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ + else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ + else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } 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_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pandore(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + 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] = { 0 }; + 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 image as a raw data file. + /** + \param filename Filename, as a C-string. + \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false). + \note The .raw format does not store the image dimensions in the output file, so you have to keep track of them somewhere + to be able to read the file correctly afterwards. + **/ + const CImg& save_raw(const char *const filename, const bool is_multiplexed=false) const { + return _save_raw(0,filename,is_multiplexed); + } + + //! Save image as a raw data file \overloading. + /** + Same as save_raw(const char *,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { + return _save_raw(file,0,is_multiplexed); + } + + const CImg& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_raw(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!is_multiplexed) cimg::fwrite(_data,size(),nfile); + else { + CImg buf(_spectrum); + cimg_forXYZ(*this,x,y,z) { + cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c); + cimg::fwrite(buf._data,_spectrum,nfile); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a video file, using the FFmpeg library. + /** + \param filename Filename, as a C-string. + \param fps Video framerate. + \param bitrate Video bitrate. + \note + - Each slice of the instance image is considered to be a single frame of the output video file. + - This method uses functions provided by the FFmpeg library. + Configuration macro \c cimg_use_ffmpeg must be set for the method to succeed natively. + Otherwise, the method calls save_ffmpeg_external(const char*,unsigned int,unsigned int,const char*,unsigned int,unsigned int) const. + **/ + const CImg& save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_ffmpeg(): Specified filename is (null).", + cimg_instance); + if (!fps) + throw CImgArgumentException(_cimg_instance + "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.", + cimg_instance, + filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_ffmpeg + return save_ffmpeg_external(filename,0,fps,bitrate); +#else + CImgList list; + get_split('z').move_to(list); + list.save_ffmpeg(filename,fps,bitrate); +#endif + return *this; + } + + //! Save image as a .yuv video file. + /** + \param filename Filename, as a C-string. + \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). + \note Each slice of the instance image is considered to be a single frame of the output video file. + **/ + const CImg& save_yuv(const char *const filename, const bool is_rgb=true) const { + get_split('z').save_yuv(filename,is_rgb); + return *this; + } + + //! Save image as a .yuv video file \overloading. + /** + Same as save_yuv(const char*,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_yuv(std::FILE *const file, const bool is_rgb=true) const { + get_split('z').save_yuv(file,is_rgb); + return *this; + } + + //! Save 3d object as an Object File Format (.off) file. + /** + \param filename Filename, as a C-string. + \param primitives List of 3d object primitives. + \param colors List of 3d object colors. + \note + - Instance image contains the vertices data of the 3d object. + - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format. + Such primitives will be lost or simplified during file saving. + - The .off file format is described here. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + const char *const filename) const { + return _save_off(primitives,colors,0,filename); + } + + //! Save 3d object as an Object File Format (.off) file \overloading. + /** + Same as save_off(const CImgList&,const CImgList&,const char*) const + with a file stream argument instead of a filename string. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file) const { + return _save_off(primitives,colors,file,0); + } + + template + const CImg& _save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_off(): Specified filename is (null).", + cimg_instance); + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "save_off(): Empty instance, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + CImgList opacities; + char error_message[1024] = { 0 }; + if (!is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgInstanceException(_cimg_instance + "save_off(): Invalid specified 3d object, for file '%s' (%s).", + cimg_instance, + filename?filename:"(FILE*)",error_message); + + const CImg default_color(1,3,1,1,200); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + unsigned int supported_primitives = 0; + cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives; + std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width); + cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const CImg& color = l1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f; + switch (psiz) { + case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break; + case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 3 : 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 : 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; + case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 6 : { + const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3); + const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 9 : { + const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4); + const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + 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),rt,gt,bt); + } break; + case 12 : { + const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5); + const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + 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),rt,gt,bt); + } break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save volumetric image as a video, using ffmpeg external binary. + /** + \param filename Filename, as a C-string. + \param codec Video codec, as a C-string. + \param fps Video framerate. + \param bitrate Video bitrate. + \note + - Each slice of the instance image is considered to be a single frame of the output video file. + - This method uses \c ffmpeg, an external executable binary provided by FFmpeg. + It must be installed for the method to succeed. + **/ + const CImg& save_ffmpeg_external(const char *const filename, const char *const codec=0, + const unsigned int fps=25, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_ffmpeg_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImgList list; + get_split('z').move_to(list); + list.save_ffmpeg_external(filename,codec,fps,bitrate); + return *this; + } + + //! Save image using gzip external binary. + /** + \param filename Filename, as a C-string. + \note This method uses \c gzip, an external executable binary provided by gzip. + It must be installed for the method to succeed. + **/ + const CImg& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_gzip_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save(filetmp); + cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filetmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimg_instance, + filename); + + else cimg::fclose(file); + std::remove(filetmp); + return *this; + } + + //! Save image using GraphicsMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c gm, an external executable binary provided by GraphicsMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_png +#define _cimg_sge_ext1 "png" +#define _cimg_sge_ext2 "png" +#else +#define _cimg_sge_ext1 "pgm" +#define _cimg_sge_ext2 "ppm" +#endif + char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); +#ifdef cimg_use_png + save_png(filetmp); +#else + save_pnm(filetmp); +#endif + cimg_snprintf(command,sizeof(command),"%s convert -quality %u \"%s\" \"%s\"", + cimg::graphicsmagick_path(),quality, + CImg::string(filetmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filetmp); + return *this; + } + + //! Save image using ImageMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c convert, an external executable binary provided by ImageMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_imagemagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_png +#define _cimg_sie_ext1 "png" +#define _cimg_sie_ext2 "png" +#else +#define _cimg_sie_ext1 "pgm" +#define _cimg_sie_ext2 "ppm" +#endif + char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); +#ifdef cimg_use_png + save_png(filetmp); +#else + save_pnm(filetmp); +#endif + cimg_snprintf(command,sizeof(command),"%s -quality %u \"%s\" \"%s\"", + cimg::imagemagick_path(),quality, + CImg::string(filetmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_imagemagick_external(): Failed to save file '%s' with external command 'convert'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filetmp); + return *this; + } + + //! Save image as a Dicom file. + /** + \param filename Filename, as a C-string. + \note This method uses \c medcon, an external executable binary provided by (X)Medcon. + It must be installed for the method to succeed. + **/ + const CImg& save_medcon_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_medcon_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand()); + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save_analyze(filetmp); + cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); + cimg::system(command); + std::remove(filetmp); + cimg::split_filename(filetmp,body); + cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body); + std::remove(filetmp); + + file = std::fopen(filename,"rb"); + if (!file) { + cimg_snprintf(command,sizeof(command),"m000-%s",filename); + file = std::fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + std::rename(command,filename); + return *this; + } + + // Save image for non natively supported formats. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note + - The filename extension tells about the desired file format. + - This method tries to save the instance image as a file, using external tools from + ImageMagick or + GraphicsMagick. At least one of these tool must be installed for the method to succeed. + - It is recommended to use the generic method save(const char*, int) const instead, as it can handle some file formats natively. + **/ + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_other(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + const unsigned int omode = 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() = omode; + if (!is_saved) + throw CImgIOException(_cimg_instance + "save_other(): Failed to save file '%s'. Format is not natively supported, and no external commands succeeded.", + cimg_instance, + filename); + return *this; + } + + // [internal] Return a 40x38 color logo of a 'danger' item. + static CImg _logo40x38() { + static bool first_time = true; + static CImg res(40,38,1,3); + if (first_time) { + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2); + for (unsigned long off = 0; off<(unsigned long)res._width*res._height;) { + const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++); + for (unsigned int l = 0; l structure + # + # + # + #------------------------------------------ + */ + //! Represent a list of images CImg. + template + struct CImgList { + unsigned int _width, _allocated_width; + CImg *_data; + + //! Simple iterator type, to loop through each image of a list. + /** + \note + - The \c CImgList::iterator type is defined as a CImg*. + - You may use it like this: + \code + CImgList<> list; // Assuming this image list is not empty. + for (CImgList<>::iterator it = list.begin(); it* iterator; + + //! Simple const iterator type, to loop through each image of a \c const list instance. + /** + \note + - The \c CImgList::const_iterator type is defined to be a const CImg*. + - Similar to CImgList::iterator, but for constant list instances. + **/ + typedef const CImg* const_iterator; + + //! Pixel value type. + /** + Refer to the pixels value type of the images in the list. + \note + - The \c CImgList::value_type type of a \c CImgList is defined to be a \c T. It is then similar to CImg::value_type. + - \c CImgList::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common T-dependant types. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif +#ifdef cimglist_plugin1 +#include cimglist_plugin1 +#endif +#ifdef cimglist_plugin2 +#include cimglist_plugin2 +#endif +#ifdef cimglist_plugin3 +#include cimglist_plugin3 +#endif +#ifdef cimglist_plugin4 +#include cimglist_plugin4 +#endif +#ifdef cimglist_plugin5 +#include cimglist_plugin5 +#endif +#ifdef cimglist_plugin6 +#include cimglist_plugin6 +#endif +#ifdef cimglist_plugin7 +#include cimglist_plugin7 +#endif +#ifdef cimglist_plugin8 +#include cimglist_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + Destroy current list instance. + \note + - Any allocated buffer is deallocated. + - Destroying an empty list does nothing actually. + **/ + ~CImgList() { + delete[] _data; + } + + //! Default constructor. + /** + Construct a new empty list instance. + \note + - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its image buffer pointer data(). + - An empty list may be reassigned afterwards, with the family of the assign() methods. In all cases, the type of pixels stays \c T. + **/ + CImgList(): + _width(0),_allocated_width(0),_data(0) {} + + //! Construct list containing empty images. + /** + \param n Number of empty images. + \note Useful when you know by advance the number of images you want to manage, as + it will allocate the right amount of memory for the list, without needs for reallocation + (that may occur when starting from an empty list and inserting several images in it). + **/ + explicit CImgList(const unsigned int n):_width(n) { + if (n) _data = new CImg[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))]; + else { _allocated_width = 0; _data = 0; } + } + + //! Construct list containing images of specified size. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \note Pixel values are not initialized and may probably contain garbage. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + } + + //! Construct list containing images of specified size, and initialize pixel values. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val Initialization value for images pixels. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T val): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val0 First value of the initializing integers sequence. + \param val1 Second value of the initializing integers sequence. + \warning You must specify at least width*height*depth*spectrum values in your argument list, or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...): + _width(0),_allocated_width(0),_data(0) { +#define _CImgList_stdarg(t) { \ + assign(n,width,height,depth,spectrum); \ + const unsigned long siz = (unsigned long)width*height*depth*spectrum, nsiz = siz*n; \ + T *ptrd = _data->_data; \ + va_list ap; \ + va_start(ap,val1); \ + for (unsigned long l = 0, s = 0, i = 0; iwidth*height*depth*spectrum values in your argument list, or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...): + _width(0),_allocated_width(0),_data(0) { + _CImgList_stdarg(double); + } + + //! Construct list containing copies of an input image. + /** + \param n Number of images. + \param img Input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img. + **/ + template + CImgList(const unsigned int n, const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + } + + //! Construct list from one image. + /** + \param img Input image to copy in the constructed list. + \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img. + **/ + template + explicit CImgList(const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(1); + _data[0].assign(img,is_shared); + } + + //! Construct list from two images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + } + + //! Construct list from three images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + } + + //! Construct list from four images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + } + + //! Construct list from five images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); + } + + //! Construct list from six images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + } + + //! Construct list from seven images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); + } + + //! Construct list from eight images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param img8 Eighth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + } + + //! Construct list copy. + /** + \param list Input list to copy. + \note The shared state of each element of the constructed list is kept the same as in \c list. + **/ + template + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + } + + //! Construct list copy \specialization. + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); + } + + //! Construct list copy, and force the shared state of the list elements. + /** + \param list Input list to copy. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImgList& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],is_shared); + } + + //! Construct list by reading the content of a file. + /** + \param filename Filename, as a C-string. + **/ + explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) { + assign(filename); + } + + //! Construct list from the content of a display window. + /** + \param disp Display window to get content from. + \note Constructed list contains a single image only. + **/ + explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) { + assign(disp); + } + + //! Return a list with elements being shared copies of images in the list instance. + /** + \note list2 = list1.get_shared() is equivalent to list2.assign(list1,true). + **/ + CImgList get_shared() { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Return a list with elements being shared copies of images in the list instance \const. + const CImgList get_shared() const { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Destructor \inplace. + /** + \see CImgList(). + **/ + CImgList& assign() { + delete[] _data; + _width = _allocated_width = 0; + _data = 0; + return *this; + } + + //! Destructor \inplace. + /** + Equivalent to assign(). + \note Only here for compatibility with STL naming conventions. + **/ + CImgList& clear() { + return assign(); + } + + //! Construct list containing empty images \inplace. + /** + \see CImgList(unsigned int). + **/ + CImgList& assign(const unsigned int n) { + if (!n) return assign(); + if (_allocated_width(n<<2)) { + delete[] _data; + _data = new CImg[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))]; + } + _width = n; + return *this; + } + + //! Construct list containing images of specified size \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T val) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) { + _CImgList_stdarg(int); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const double, const double, ...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...) { + _CImgList_stdarg(double); + return *this; + } + + //! Construct list containing copies of an input image \inplace. + /** + \see CImgList(unsigned int, const CImg&, bool). + **/ + template + CImgList& assign(const unsigned int n, const CImg& img, const bool is_shared=false) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + return *this; + } + + //! Construct list from one image \inplace. + /** + \see CImgList(const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img, const bool is_shared=false) { + assign(1); + _data[0].assign(img,is_shared); + return *this; + } + + //! Construct list from two images \inplace. + /** + \see CImgList(const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const bool is_shared=false) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + return *this; + } + + //! Construct list from three images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + return *this; + } + + //! Construct list from four images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + return *this; + } + + //! Construct list from five images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); + return *this; + } + + //! Construct list from six images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + return *this; + } + + //! Construct list from seven images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); + return *this; + } + + //! Construct list from eight images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared); + _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + return *this; + } + + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. + /** + \see CImgList(const CImgList&, bool is_shared). + **/ + template + CImgList& assign(const CImgList& list, const bool is_shared=false) { + cimg::unused(is_shared); + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + return *this; + } + + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace \specialization. + CImgList& assign(const CImgList& list, const bool is_shared=false) { + if (this==&list) return *this; + CImgList res(list._width); + cimglist_for(res,l) res[l].assign(list[l],is_shared); + return res.move_to(*this); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& assign(const CImgDisplay &disp) { + return assign(CImg(disp)); + } + + //! Transfer the content of the list instance to another list. + /** + \param list Destination list. + \note When returning, the current list instance is empty and the initial content of \c list is destroyed. + **/ + template + CImgList& move_to(CImgList& list) { + list.assign(_width); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[l]); + assign(); + return list; + } + + //! Transfer the content of the list instance at a specified position in another list. + /** + \param list Destination list. + \param pos Index of the insertion in the list. + \note When returning, the list instance is empty and the initial content of \c list is preserved + (only images indexes may be modified). + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos) { + if (is_empty()) return list; + const unsigned int npos = pos>list._width?list._width:pos; + list.insert(_width,npos); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[npos+l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[npos+l]); + assign(); + return list; + } + + //! Swap all fields between two list instances. + /** + \param list List to swap fields with. + \note Can be used to exchange the content of two lists in a fast way. + **/ + CImgList& swap(CImgList& list) { + cimg::swap(_width,list._width); + cimg::swap(_allocated_width,list._allocated_width); + cimg::swap(_data,list._data); + return list; + } + + //! Return a reference to an empty list. + /** + \note Can be used to define default values in a function taking a CImgList as an argument. + \code + void f(const CImgList& list=CImgList::empty()); + \endcode + **/ + static CImgList& empty() { + static CImgList _empty; + return _empty.assign(); + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Return a reference to one image element of the list. + /** + \param pos Indice of the image element. + **/ + CImg& operator()(const unsigned int pos) { +#if cimg_verbosity>=3 + if (pos>=_width) { + cimg::warn(_cimglist_instance + "operator(): Invalid image request, at position [%u].", + cimglist_instance, + pos); + return *_data; + } +#endif + return _data[pos]; + } + + //! Return a reference to one image of the list. + /** + \param pos Indice of the image element. + **/ + const CImg& operator()(const unsigned int pos) const { + return const_cast*>(this)->operator()(pos); + } + + //! Return a reference to one pixel value of one image of the list. + /** + \param pos Indice of the image element. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list(n,x,y,z,c) is equivalent to list[n](x,y,z,c). + **/ + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + return (*this)[pos](x,y,z,c); + } + + //! Return a reference to one pixel value of one image of the list \const. + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return (*this)[pos](x,y,z,c); + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + operator CImg*() { + return _data; + } + + //! Return pointer to the first image of the list \const. + operator const CImg*() const { + return _data; + } + + //! Construct list from one image \inplace. + /** + \param img Input image to copy in the constructed list. + \note list = img; is equivalent to list.assign(img);. + **/ + template + CImgList& operator=(const CImg& img) { + return assign(img); + } + + //! Construct list from another list. + /** + \param list Input list to copy. + \note list1 = list2 is equivalent to list1.assign(list2);. + **/ + template + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list from another list \specialization. + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& operator=(const char *const filename) { + return assign(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return a non-shared copy of a list. + /** + \note +list is equivalent to CImgList(list,false). It forces the copy to have non-shared elements. + **/ + CImgList operator+() const { + return CImgList(*this,false); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end. + /** + \param img Image inserted at the end of the instance copy. + \note Define a convenient way to create temporary lists of images, as in the following code: + \code + (img1,img2,img3,img4).display("My four images"); + \endcode + **/ + template + CImgList& operator,(const CImg& img) { + return insert(img); + } + + //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end. + /** + \param list List inserted at the end of the instance copy. + **/ + template + CImgList& operator,(const CImgList& list) { + return insert(list); + } + + //! Return image corresponding to the appending of all images of the instance list along specified axis. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \note list>'x' is equivalent to list.get_append('x'). + **/ + CImg operator>(const char axis) const { + return get_append(axis,0); + } + + //! Return list corresponding to the splitting of all images of the instance list along specified axis. + /** + \param axis Axis used for image splitting. + \note list<'x' is equivalent to list.get_split('x'). + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to size() but returns result as a (signed) integer. + **/ + int width() const { + return (int)_width; + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to width() but returns result as an unsigned integer. + **/ + unsigned int size() const { + return _width; + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + CImg *data() { + return _data; + } + + //! Return pointer to the first image of the list \const. + const CImg *data() const { + return _data; + } + + //! Return pointer to the pos-th image of the list. + /** + \param pos Indice of the image element to access. + \note list.data(n); is equivalent to list.data + n;. + **/ +#if cimg_verbosity>=3 + CImg *data(const unsigned int pos) { + if (pos>=size()) + cimg::warn(_cimglist_instance + "data(): Invalid pointer request, at position [%u].", + cimglist_instance, + pos); + return _data + pos; + } + + const CImg *data(const unsigned int l) const { + return const_cast*>(this)->data(l); + } +#else + CImg *data(const unsigned int l) { + return _data + l; + } + + //! Return pointer to the pos-th image of the list \const. + const CImg *data(const unsigned int l) const { + return _data + l; + } +#endif + + //! Return iterator to the first image of the list. + /** + **/ + iterator begin() { + return _data; + } + + //! Return iterator to the first image of the list \const. + const_iterator begin() const { + return _data; + } + + //! Return iterator to one position after the last image of the list. + /** + **/ + iterator end() { + return _data + _width; + } + + //! Return iterator to one position after the last image of the list \const. + const_iterator end() const { + return _data + _width; + } + + //! Return reference to the first image of the list. + /** + **/ + CImg& front() { + return *_data; + } + + //! Return reference to the first image of the list \const. + const CImg& front() const { + return *_data; + } + + //! Return a reference to the last image of the list. + /** + **/ + const CImg& back() const { + return *(_data + _width - 1); + } + + //! Return a reference to the last image of the list \const. + CImg& back() { + return *(_data + _width - 1); + } + + //! Return pos-th image of the list. + /** + \param pos Indice of the image element to access. + **/ + CImg& at(const int pos) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "at(): Empty instance.", + cimglist_instance); + + return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos]; + } + + //! Access to pixel value with Dirichlet boundary conditions. + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions. + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c); + } + + T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c); + } + + T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c); + } + + T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x) \const. + T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x) \const. + T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c); + } + + T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the first coordinates (\c pos). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the first coordinates (\c pos) \const. + T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the first coordinates (\c pos). + /** + \param pos Indice of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the first coordinates (\c pos) \const. + T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c); + } + + T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c); + } + + //! Return a C-string containing the values of all images in the instance list. + /** + \param separator Character separator set between consecutive pixel values. + \param max_size Maximum size of the returned string. + \note The result is returne as a CImg image whose pixel buffer contains the desired C-string. + **/ + CImg value_string(const char separator=',', const unsigned int max_size=0) const { + if (is_empty()) return CImg(1,1,1,1,0); + CImgList items; + for (unsigned int l = 0; l<_width-1; ++l) { + CImg item = _data[l].value_string(separator,0); + item.back() = separator; + item.move_to(items); + } + _data[_width-1].value_string(separator,0).move_to(items); + CImg res; (items>'x').move_to(res); + if (max_size) { res.crop(0,max_size); res(max_size) = 0; } + return res; + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Return \c true if list is empty. + /** + **/ + bool is_empty() const { + return (!_data || !_width); + } + + //! Test if number of image elements is equal to specified value. + /** + \param size_n Number of image elements to test. + **/ + bool is_sameN(const unsigned int size_n) const { + return _width==size_n; + } + + //! Test if number of image elements is equal between two images lists. + /** + \param list Input list to compare with. + **/ + template + bool is_sameN(const CImgList& list) const { + return is_sameN(list._width); + } + + // Define useful functions to check list dimensions. + // (cannot be documented because 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<_width && 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<_width && 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<_width && 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 bool is_same##axis(const CImg& img) const { \ + bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \ + } \ + template bool is_same##axis(const CImgList& list) const { \ + const unsigned int lmin = cimg::min(_width,list._width); \ + bool res = true; for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ + return (is_sameN(n) && is_same##axis(img)); \ + } \ + template bool is_sameN##axis(const CImgList& list) const { \ + return (is_sameN(list) && is_same##axis(list)); \ + } + + _cimglist_def_is_same(XY) + _cimglist_def_is_same(XZ) + _cimglist_def_is_same(XC) + _cimglist_def_is_same(YZ) + _cimglist_def_is_same(YC) + _cimglist_def_is_same(XYZ) + _cimglist_def_is_same(XYC) + _cimglist_def_is_same(YZC) + _cimglist_def_is_same(XYZC) + _cimglist_def_is_same1(X) + _cimglist_def_is_same1(Y) + _cimglist_def_is_same1(Z) + _cimglist_def_is_same1(C) + _cimglist_def_is_same2(X,Y) + _cimglist_def_is_same2(X,Z) + _cimglist_def_is_same2(X,C) + _cimglist_def_is_same2(Y,Z) + _cimglist_def_is_same2(Y,C) + _cimglist_def_is_same2(Z,C) + _cimglist_def_is_same3(X,Y,Z) + _cimglist_def_is_same3(X,Y,C) + _cimglist_def_is_same3(X,Z,C) + _cimglist_def_is_same3(Y,Z,C) + + //! Test if dimensions of each image of the list match specified arguments. + /** + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const { + bool res = true; + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc); + return res; + } + + //! Test if list dimensions match specified arguments. + /** + \param n Number of images in the list. + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const { + return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc); + } + + //! Test if list contains one particular pixel location. + /** + \param n Index of the image whom checked pixel value belong to. + \param x X-coordinate of the checked pixel value. + \param y Y-coordinate of the checked pixel value. + \param z Z-coordinate of the checked pixel value. + \param c C-coordinate of the checked pixel value. + **/ + bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) return false; + return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() && + z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum(); + } + + //! Test if list contains image with specified indice. + /** + \param n Index of the checked image. + **/ + bool containsN(const int n) const { + if (is_empty()) return false; + return n>=0 && n<(int)_width; + } + + //! Test if one image of the list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \param[out] z Z-coordinate of the pixel value, if test succeeds. + \param[out] c C-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y,z,c). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { + if (is_empty()) return false; + cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; } + return false; + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \param[out] z Z-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y,z). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { + t c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y) const { + t z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x). + **/ + template + bool contains(const T& pixel, t& n, t& x) const { + t y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \note If true, set coordinates (n). + **/ + template + bool contains(const T& pixel, t& n) const { + t x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + **/ + bool contains(const T& pixel) const { + unsigned int n, x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if the list contains the image 'img'. + /** + \param img Reference to image to test. + \param[out] n Index of image in the list, if test succeeds. + \note If true, returns the position (n) of the image in the list. + **/ + template + bool contains(const CImg& img, t& n) const { + if (is_empty()) return false; + const CImg *const ptr = &img; + cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; } + return false; + } + + //! Test if the list contains the image img. + /** + \param img Reference to image to test. + **/ + bool contains(const CImg& img) const { + unsigned int n; + return contains(img,n); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + //! Return a reference to the minimum pixel value of the instance list. + /** + **/ + T& min() { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "min(): Empty instance.", + cimglist_instance); + T *ptr_min = _data->_data; + T min_value = *ptr_min; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs_data; + T min_value = *ptr_min; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs_data; + T max_value = *ptr_max; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the maximum pixel value of the instance list \const. + const T& max() const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "max(): Empty instance.", + cimglist_instance); + const T *ptr_max = _data->_data; + T max_value = *ptr_max; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + T& min_max(t& max_val) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "min_max(): Empty instance.", + cimglist_instance); + T *ptr_min = _data->_data; + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + const T& min_max(t& max_val) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "min_max(): Empty instance.", + cimglist_instance); + const T *ptr_min = _data->_data; + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well. + /** + \param[out] min_val Value of the minimum value found. + **/ + template + T& max_min(t& min_val) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "max_min(): Empty instance.", + cimglist_instance); + T *ptr_max = _data->_data; + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "max_min(): Empty instance.", + cimglist_instance); + const T *ptr_max = _data->_data; + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + if (is_shared) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified shared image CImg<%s>(%u,%u,%u,%u,%p) at position %u " + "(pixel types are different).", + cimglist_instance, + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos); + + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list. + _data = new_data; + *_data = img; + } else { + if (new_data) { // Insert with re-allocation. + if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); + if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); + std::memset(_data,0,sizeof(CImg)*(_width-1)); + delete[] _data; + _data = new_data; + } else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); // Insert without re-allocation. + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0; + _data[npos] = img; + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list. + _data = new_data; + if (is_shared && img) { + _data->_width = img._width; _data->_height = img._height; _data->_depth = img._depth; _data->_spectrum = img._spectrum; + _data->_is_shared = true; _data->_data = img._data; + } else *_data = img; + } + else { + if (new_data) { // Insert with re-allocation. + if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); + if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); + if (is_shared && img) { + new_data[npos]._width = img._width; new_data[npos]._height = img._height; new_data[npos]._depth = img._depth; + new_data[npos]._spectrum = img._spectrum; 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]._spectrum = 0; new_data[npos]._data = 0; + new_data[npos] = img; + } + std::memset(_data,0,sizeof(CImg)*(_width-1)); + delete[] _data; + _data = new_data; + } else { // Insert without re-allocation. + if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); + if (is_shared && img) { + _data[npos]._width = img._width; _data[npos]._height = img._height; _data[npos]._depth = img._depth; _data[npos]._spectrum = img._spectrum; + _data[npos]._is_shared = true; _data[npos]._data = img._data; + } else { + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0; + _data[npos] = img; + } + } + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. + template + CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(img,pos,is_shared); + } + + //! Insert n empty images img into the current image list, at position \p pos. + /** + \param n Number of empty images to insert. + \param pos Index of the insertion. + **/ + CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { + CImg empty; + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { + return (+*this).insert(n,pos); + } + + //! Insert \c n copies of the image \c img into the current image list, at position \c pos. + /** + \param n Number of image copies to insert. + \param img Image to insert by copy. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of \c img or not. + **/ + template + CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + insert(img,npos,is_shared); + for (unsigned int i = 1; i + CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(n,img,pos,is_shared); + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos. + /** + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,is_shared); + else insert(CImgList(list),npos,is_shared); + return *this; + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance. + template + CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(list,pos,is_shared); + } + + //! Insert n copies of the list \c list at position \c pos of the current list. + /** + \param n Number of list copies to insert. + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(n,list,pos,is_shared); + } + + //! Remove all images between from indexes. + /** + \param pos1 Starting index of the removal. + \param pos2 Ending index of the removal. + **/ + CImgList& remove(const unsigned int pos1, const unsigned int pos2) { + const unsigned int + npos1 = pos1=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + else { + if (tpos2>=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + + for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign(); + const unsigned int nb = 1 + npos2 - npos1; + if (!(_width-=nb)) return assign(); + if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation. + if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg)*(_width - npos1)); + std::memset(_data + _width,0,sizeof(CImg)*nb); + } else { // Removing items with reallocation. + _allocated_width>>=2; + while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; + CImg *const new_data = new CImg[_allocated_width]; + if (npos1) std::memcpy(new_data,_data,sizeof(CImg)*npos1); + if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg)*(_width-npos1)); + if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width)); + std::memset(_data,0,sizeof(CImg)*(_width+nb)); + delete[] _data; + _data = new_data; + } + } + return *this; + } + + //! Remove all images between from indexes \newinstance. + CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { + return (+*this).remove(pos1,pos2); + } + + //! Remove image at index \c pos from the image list. + /** + \param pos Index of the image to remove. + **/ + CImgList& remove(const unsigned int pos) { + return remove(pos,pos); + } + + //! Remove image at index \c pos from the image list \newinstance. + CImgList get_remove(const unsigned int pos) const { + return (+*this).remove(pos); + } + + //! Remove last image. + /** + **/ + CImgList& remove() { + return remove(_width-1); + } + + //! Remove last image \newinstance. + CImgList get_remove() const { + return (+*this).remove(); + } + + //! Reverse list order. + CImgList& reverse() { + for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]); + return *this; + } + + //! Reverse list order \newinstance. + CImgList get_reverse() const { + return (+*this).reverse(); + } + + //! Return a sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList& images(const unsigned int pos0, const unsigned int pos1) { + return get_images(pos0,pos1).move_to(*this); + } + + //! Return a sublist \newinstance. + CImgList get_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1-pos0+1); + cimglist_for(res,l) res[l].assign(_data[pos0+l]); + return res; + } + + //! Return a shared sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1-pos0+1); + cimglist_for(res,l) res[l].assign(_data[pos0+l],_data[pos0+l]?true:false); + return res; + } + + //! Return a shared sublist \newinstance. + const CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1-pos0+1); + cimglist_for(res,l) res[l].assign(_data[pos0+l],_data[pos0+l]?true:false); + return res; + } + + //! Return a single image which is the appending of all images of the current CImgList instance. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg get_append(const char axis, const float align=0) const { + if (is_empty()) return CImg(); + if (_width==1) return +((*this)[0]); + unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; + CImg res; + switch (cimg::uncase(axis)) { + case 'x' : { // Along the X-axis. + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); } + } + res.assign(dx,dy,dz,dc,0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) res.draw_image(pos, + (int)(align*(dy-img._height)), + (int)(align*(dz-img._depth)), + (int)(align*(dc-img._spectrum)), + img); + pos+=img._width; + } + } break; + case 'y' : { // Along the Y-axis. + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); } + } + res.assign(dx,dy,dz,dc,0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) res.draw_image((int)(align*(dx-img._width)), + pos, + (int)(align*(dz-img._depth)), + (int)(align*(dc-img._spectrum)), + img); + pos+=img._height; + } + } break; + case 'z' : { // Along the Z-axis. + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum); } + } + res.assign(dx,dy,dz,dc,0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) res.draw_image((int)(align*(dx-img._width)), + (int)(align*(dy-img._height)), + pos, + (int)(align*(dc-img._spectrum)), + img); + pos+=img._depth; + } + } break; + default : { // Along the C-axis. + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum; } + } + res.assign(dx,dy,dz,dc,0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) res.draw_image((int)(align*(dx-img._width)), + (int)(align*(dy-img._height)), + (int)(align*(dz-img._depth)), + pos, + img); + pos+=img._spectrum; + } + } + } + return res; + } + + //! Return a list where each image has been split along the specified axis. + /** + \param axis Axis to split images along. + \param nb Number of spliting parts for each image. + **/ + CImgList& split(const char axis, const int nb=0) { + return get_split(axis,nb).move_to(*this); + } + + //! Return a list where each image has been split along the specified axis \newinstance. + CImgList get_split(const char axis, const int nb=0) const { + CImgList res; + cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); + return res; + } + + //! Insert image at the end of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image at the front of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list at the end of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list at the front of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Remove last image. + /** + **/ + CImgList& pop_back() { + return remove(_width-1); + } + + //! Remove first image. + /** + **/ + CImgList& pop_front() { + return remove(0); + } + + //! Remove image pointed by iterator. + /** + \param iter Iterator pointing to the image to remove. + **/ + CImgList& erase(const iterator iter) { + return remove(iter-_data); + } + + //@} + //---------------------------------- + // + //! \name Data Input + //@{ + //---------------------------------- + + //! Display a simple interactive interface to select images or sublists. + /** + \param disp Window instance to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(CImgDisplay &disp, const bool feature_type=true, + const char axis='x', const float align=0) const { + return _get_select(disp,0,feature_type,axis,align,0,false,false,false); + } + + //! Display a simple interactive interface to select images or sublists. + /** + \param title Title of a new window used to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(const char *const title, const bool feature_type=true, + const char axis='x', const float align=0) const { + CImgDisplay disp; + return _get_select(disp,title,feature_type,axis,align,0,false,false,false); + } + + CImg _get_select(CImgDisplay &disp, const char *const title, const bool feature_type, + const char axis, const float align, + const unsigned int orig, const bool resize_disp, + const bool exit_on_rightbutton, const bool exit_on_wheel) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "select(): Empty instance.", + cimglist_instance); + + // Create image correspondence table and get list dimensions for visualization. + CImgList _indices; + unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + if (h>max_height) max_height = h; + sum_width+=w; sum_height+=h; + if (axis=='x') CImg(w,1,1,1,(unsigned int)l).move_to(_indices); + else CImg(h,1,1,1,(unsigned int)l).move_to(_indices); + } + const CImg indices0 = _indices>'x'; + + // Create display window. + if (!disp) { + if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else if (title) disp.set_title("%s",title); + if (resize_disp) { + if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false); + else disp.resize(cimg_fitscreen(max_width,sum_height,1),false); + } + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0); + const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + + // Enter event loop. + CImg visu0, visu; + CImg indices; + CImg positions(_width,4,1,1,-1); + int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1; + bool is_clicked = false, is_selected = false, text_down = false, update_display = true; + unsigned int key = 0; + while (!is_selected && !disp.is_closed() && !key) { + + // Create background image. + if (!visu0) { + visu0.assign(disp._width,disp._height,1,3,0); visu.assign(); + (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices); + unsigned int ind = 0; + if (axis=='x') for (unsigned int x = 0; x + onexone(1,1,1,1,0), + &src = _data[ind]?_data[ind]:onexone, + _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg(), + &img2d = _img2d?_img2d:src; + CImg res = old_normalization==1 || (old_normalization==3 && cimg::type::string()!=cimg::type::string())? + CImg(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1)).normalize(0,255)): + CImg(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1))); + const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true); + res.resize(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)x0; + positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height()-res.height())); + positions(ind,2)+=res._width; + positions(ind,3)+=res._height - 1; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } else for (unsigned int y = 0; y + &src = _data[ind], + _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg(), + &img2d = _img2d?_img2d:src; + CImg res = old_normalization==1 || (old_normalization==3 && cimg::type::string()!=cimg::type::string())? + CImg(img2d.get_normalize(0,255)): + CImg(img2d); + if (res._spectrum>3) res.channels(0,2); + const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false); + res.resize(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width())); + positions(ind,1) = positions(ind,3) = (int)y0; + positions(ind,2)+=res._width - 1; + positions(ind,3)+=res._height; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + if (axis=='x') --positions(ind,2); else --positions(ind,3); + update_display = true; + } + + if (!visu || oindice0!=indice0 || oindice1!=indice1) { + if (indice0>=0 && indice1>=0) { + visu.assign(visu0,false); + const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1); + for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) { + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),background_color,0.2f); + if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) || + (axis!='x' && positions(ind,3) - positions(ind,1)>=8)) + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),foreground_color,0.9f,0x55555555); + } + const int yt = (int)text_down?visu.height()-13:0; + if (is_clicked) visu.draw_text(0,yt," Images %u - %u, Size = %u",foreground_color,background_color,0.7f,13, + orig + indm,orig + indM,indM - indm + 1); + else visu.draw_text(0,yt," Image %u",foreground_color,background_color,0.7f,13, + orig + indice0); + update_display = true; + } else visu.assign(); + } + if (!visu) { visu.assign(visu0,true); update_display = true; } + if (update_display) { visu.display(disp); update_display = false; } + disp.wait(); + + // Manage user events. + const int xm = disp.mouse_x(), ym = disp.mouse_y(); + int indice = -1; + + if (xm>=0) { + indice = (int)indices(axis=='x'?xm:ym); + if (disp.button()&1) { + if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; } + oindice1 = indice1; indice1 = indice; + if (!feature_type) is_selected = true; + } else { + if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; } + else is_selected = true; + } + } else { + if (is_clicked) { + if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; } + else indice1 = -1; + } else indice0 = indice1 = -1; + } + + if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; } + if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; } + if (disp.wheel() && exit_on_wheel) is_selected = true; + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).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.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp); + visu0.save(filename); + visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + } + disp.set_key(key,false).wait(); key = 0; + } break; + case cimg::keyO : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp); + save(filename); + visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + disp.set_key(key,false).wait(); key = 0; + } break; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} + else if (ym>=visu.height()-13) { if(text_down) { visu.assign(); text_down = false; }} + } + CImg res(1,2,1,1,-1); + if (is_selected) { if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1)); else res.fill(indice0); } + if (!(disp.button()&2)) disp.set_button(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + disp.set_key(key); + return res; + } + + //! Load a list from a file. + /** + \param filename Filename to read data from. + **/ + CImgList& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load(): Specified filename is (null).", + cimglist_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + char filename_local[1024] = { 0 }; + load(cimg::load_network_external(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + 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); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) load_cimg(filename); + else if (!cimg::strcasecmp(ext,"rec") || + !cimg::strcasecmp(ext,"par")) load_parrec(filename); + else 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,"ogg") || + !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); + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + else throw CImgIOException("CImgList<%s>::load()", + pixel_type()); + } catch (CImgIOException&) { + try { + cimg::fclose(cimg::fopen(filename,"rb")); + } catch (CImgIOException&) { + cimg::exception_mode() = omode; + throw CImgIOException(_cimglist_instance + "load(): Failed to open file '%s'.", + cimglist_instance, + filename); + } + assign(1); + try { + _data->load(filename); + } catch (CImgIOException&) { + cimg::exception_mode() = omode; + throw CImgIOException(_cimglist_instance + "load(): Failed to recognize format of file '%s'.", + cimglist_instance, + filename); + } + } + cimg::exception_mode() = omode; + return *this; + } + + //! Load a list from a file \newinstance. + static CImgList get_load(const char *const filename) { + return CImgList().load(filename); + } + + //! Load a list from a .cimg file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_cimg(const char *const filename) { + return _load_cimg(0,filename); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename) { + return CImgList().load_cimg(filename); + } + + //! Load a list from a .cimg file. + /** + \param file File to read data from. + **/ + CImgList& load_cimg(std::FILE *const file) { + return _load_cimg(file,0); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file) { + return CImgList().load_cimg(file); + } + + CImgList& _load_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,C); \ + unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ + delete[] cbuf; \ + const Tss *ptrs = raw._data; \ + for (unsigned long off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \ +} +#else +#define _cimgz_load_cimg_case(Tss) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ + cimglist_instance, \ + filename?filename:"(FILE*)"); +#endif + +#define _cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l=0) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; csiz = 0; \ + if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:("(FILE*)")); \ + if (W*H*D*C>0) { \ + CImg raw; \ + CImg &img = _data[l]; \ + img.assign(W,H,D,C); \ + T *ptrd = img._data; \ + if (err==5) _cimgz_load_cimg_case(Tss) \ + else for (long to_read = (long)img.size(); to_read>0; ) { \ + raw.assign(cimg::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ + to_read-=raw._width; \ + const Tss *ptrs = raw._data; \ + for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + + const int cimg_iobuffer = 12*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; + unsigned int j, err, N = 0, W, H, D, C, csiz; + int i; + do { + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + } while (*tmp=='#' && i!=EOF); + err = 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_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + 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",unsigned char); + _cimg_load_cimg_case("uchar",unsigned char); + _cimg_load_cimg_case("char",char); + _cimg_load_cimg_case("unsigned_short",unsigned short); + _cimg_load_cimg_case("ushort",unsigned short); + _cimg_load_cimg_case("short",short); + _cimg_load_cimg_case("unsigned_int",unsigned int); + _cimg_load_cimg_case("uint",unsigned int); + _cimg_load_cimg_case("int",int); + _cimg_load_cimg_case("unsigned_long",unsigned long); + _cimg_load_cimg_case("ulong",unsigned long); + _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_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a sublist list from a (non compressed) .cimg file. + /** + \param filename Filename to read data from. + \param n0 Starting index of images to read. + \param n1 Ending index of images to read. + \param x0 Starting X-coordinates of image regions to read. + \param y0 Starting Y-coordinates of image regions to read. + \param z0 Starting Z-coordinates of image regions to read. + \param c0 Starting C-coordinates of image regions to read. + \param x1 Ending X-coordinates of image regions to read. + \param y1 Ending Y-coordinates of image regions to read. + \param z1 Ending Z-coordinates of image regions to read. + \param c1 Ending C-coordinates of image regions to read. + **/ + CImgList& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { + return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sublist list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file. + /** + \param file File to read data from. + \param n0 Starting index of images to read. + \param n1 Ending index of images to read. + \param x0 Starting X-coordinates of image regions to read. + \param y0 Starting Y-coordinates of image regions to read. + \param z0 Starting Z-coordinates of image regions to read. + \param c0 Starting C-coordinates of image regions to read. + \param x1 Ending X-coordinates of image regions to read. + \param y1 Ending Y-coordinates of image regions to read. + \param z1 Ending Z-coordinates of image regions to read. + \param c1 Ending C-coordinates of image regions to read. + **/ + CImgList& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { + return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + CImgList& _load_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 c0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { +#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=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; \ + if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:"(FILE*)"); \ + if (W*H*D*C>0) { \ + if (l=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*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, \ + nc1 = c1>=C?C-1:c1; \ + CImg raw(1 + nx1 - x0); \ + CImg &img = _data[l - n0]; \ + img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \ + T *ptrd = img._data; \ + const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ + if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v = 1 + nc1 - c0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) 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) std::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + ny1 - y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) 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) std::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ + if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ + if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (C-1-nc1)*W*H*D*sizeof(Tss); \ + if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + + if (n1%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.", + cimglist_instance, + n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; + unsigned int j, err, N, W, H, D, C; + int i; + j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = 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_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + 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",unsigned char); + _cimg_load_cimg_case2("uchar",unsigned char); + _cimg_load_cimg_case2("char",char); + _cimg_load_cimg_case2("unsigned_short",unsigned short); + _cimg_load_cimg_case2("ushort",unsigned short); + _cimg_load_cimg_case2("short",short); + _cimg_load_cimg_case2("unsigned_int",unsigned int); + _cimg_load_cimg_case2("uint",unsigned int); + _cimg_load_cimg_case2("int",int); + _cimg_load_cimg_case2("unsigned_long",unsigned long); + _cimg_load_cimg_case2("ulong",unsigned long); + _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_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_parrec(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_parrec(): Specified filename is (null).", + cimglist_instance); + + char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 }; + const char *const ext = cimg::split_filename(filename,body); + if (!std::strcmp(ext,"par")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body); } + if (!std::strcmp(ext,"PAR")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body); } + if (!std::strcmp(ext,"rec")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body); } + if (!std::strcmp(ext,"REC")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",body); } + std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + int err; + char line[256] = { 0 }; + do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.')); + do { + unsigned int sn,size_x,size_y,pixsize; + float rs,ri,ss; + err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss); + if (err==7) { + CImg::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices); + unsigned int i; for (i = 0; i::vector(size_x,size_y,sn).move_to(st_global); + else { + CImg &vec = st_global[i]; + if (size_x>vec[0]) vec[0] = size_x; + if (size_y>vec[1]) vec[1] = size_y; + vec[2] = sn; + } + st_slices[st_slices._width-1][7] = (float)i; + } + } while (err==7); + + // Read data + std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + CImg(vec[0],vec[1],vec[2]).move_to(*this); + } + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0] - 1, + pixsize = (unsigned int)vec[1], + size_x = (unsigned int)vec[2], + size_y = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default : + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException(_cimglist_instance + "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", + cimglist_instance, + pixsize,filename); + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!_width) + throw CImgIOException(_cimglist_instance + "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.", + cimglist_instance, + filename); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file \newinstance. + static CImgList get_load_parrec(const char *const filename) { + return CImgList().load_parrec(filename); + } + + //! Load a list from a YUV image sequence file. + /** + \param filename Filename to read data from. + \param size_x Width of the images. + \param size_y Height of the images. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param yuv2rgb Apply YUV to RGB transformation during reading. + **/ + CImgList& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from a YUV image sequence file \newinstance. + static CImgList get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \overloading. + CImgList& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \newinstance. + static CImgList get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + } + + CImgList& _load_yuv(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int first_frame, const unsigned int last_frame, + const unsigned int step_frame, const bool yuv2rgb) { + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified filename is (null).", + cimglist_instance); + if (size_x%2 || size_y%2) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Invalid odd XY dimensions %ux%u in file '%s'.", + cimglist_instance, + size_x,size_y,filename?filename:"(FILE*)"); + if (!size_x || !size_y) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Invalid sequence size (%u,%u) in file '%s'.", + cimglist_instance, + size_x,size_y,filename?filename:"(FILE*)"); + + const unsigned int + nfirst_frame = first_frame tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2); + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stopflag = false; + int err; + if (nfirst_frame) { + err = std::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_yuv(): File '%s' doesn't contain frame number %u.", + cimglist_instance, + 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)std::fread((void*)(tmp._data),1,(unsigned long)tmp._width*tmp._height,nfile); + if (err!=(int)(tmp._width*tmp._height)) { + stopflag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + UV.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile); + if (err!=(int)(UV.size())) { + stopflag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } 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) std::fseek(nfile,(nstep_frame-1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + } + } + } + if (stopflag && nlast_frame!=~0U && frame!=nlast_frame) + cimg::warn(_cimglist_instance + "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", + cimglist_instance, + nlast_frame,frame-1,filename?filename:"(FILE*)"); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a video file, using ffmpeg libraries. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param pixel_format To be documented. + \param resume To be documented. + **/ + // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net) + // I modified it afterwards for direct inclusion in the library core. + CImgList& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_ffmpeg(): Specified filename is (null).", + cimglist_instance); + + const unsigned int + nfirst_frame = first_frame1) || (resume && (pixel_format || !pixel_format))) + throw CImgArgumentException(_cimglist_instance + "load_ffmpeg(): Unable to load sub-frames from file '%s' unless libffmpeg is enabled.", + cimglist_instance, + filename); + + return load_ffmpeg_external(filename); +#else + const PixelFormat 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_instance + "load_ffmpeg(): Failed to resume loading of file '%s', due to unallocated FFMPEG structures.", + cimglist_instance, + filename); + } else { + // Open video file, find main video stream and codec. + if (format_ctx) avformat_close_input(&format_ctx); + if (avformat_open_input(&format_ctx,filename,0,0)!=0) + throw CImgIOException(_cimglist_instance + "load_ffmpeg(): Failed to open file '%s'.", + cimglist_instance, + filename); + + if (!avframe || !converted_frame || avformat_find_stream_info(format_ctx,NULL)<0) { + avformat_close_input(&format_ctx); format_ctx = 0; + return load_ffmpeg_external(filename); + } +#if cimg_verbosity>=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==AVMEDIA_TYPE_VIDEO) break; + if (vstream==(int)format_ctx->nb_streams) assign(); + else { + CImgList timestamps; + int nb_frames; + AVPacket packet; + // Count frames and store timestamps. + for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet)) + if (packet.stream_index==vstream) { + CImg::vector((double)packet.pts).move_to(timestamps); + ++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>'y'); + } + avformat_close_input(&format_ctx); format_ctx = 0; + return *this; + } + + for (vstream = 0; vstream<(int)(format_ctx->nb_streams) && + format_ctx->streams[vstream]->codec->codec_type!=AVMEDIA_TYPE_VIDEO; ) ++vstream; + if (vstream==(int)format_ctx->nb_streams) { + avformat_close_input(&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) { + return load_ffmpeg_external(filename); + } + if (avcodec_open2(codec_ctx,codec,NULL)<0) { // Open codec + 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; +#if defined(AV_VERSION_INT) +#if LIBAVCODEC_VERSION_INTwidth,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width, + codec_ctx->height,ffmpeg_pixfmt,1,0,0,0); + sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize); + if (ffmpeg_pixfmt==PIX_FMT_RGB24) { + CImg next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true); + next_image._get_permute_axes("yzcx",foo).move_to(*this); + } else { + CImg next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true); + next_image._get_permute_axes("yzcx",foo).move_to(*this); + } + next_frame+=nstep_frame; + } + ++frame; + } + av_free_packet(&packet); + if (next_frame>nlast_frame) break; + } + } + delete[] buffer; +#endif + return *this; + } + + //! Load an image from a video file, using ffmpeg libraries \newinstance. + static CImgList get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true) { + return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format); + } + + //! Load an image from a video file using the external tool 'ffmpeg'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_ffmpeg_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_ffmpeg_external(): Specified filename is (null).", + cimglist_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + std::FILE *file = 0; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp); + if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp); +#if cimg_OS!=2 + cimg_snprintf(command,sizeof(command),"%s -i \"%s\" \"%s\" >/dev/null 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp2)._system_strescape().data()); +#else + cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" \"%s\"\" >NUL 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp2)._system_strescape().data()); +#endif + cimg::system(command,0); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + assign(); + unsigned int i = 1; + for (bool stopflag = false; !stopflag; ++i) { + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i); + CImg img; + try { img.load_pnm(filetmp2); } + catch (CImgException&) { stopflag = true; } + if (img) { img.move_to(*this); std::remove(filetmp2); } + } + cimg::exception_mode() = omode; + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.", + cimglist_instance, + filename); + return *this; + } + + //! Load an image from a video file using the external tool 'ffmpeg' \newinstance. + static CImgList get_load_ffmpeg_external(const char *const filename) { + return CImgList().load_ffmpeg_external(filename); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools. + /** + \param filename Filename to read data from. + \param use_graphicsmagick Tells if GraphicsMagick's tool 'gm' is used instead of ImageMagick's tool 'convert'. + **/ + CImgList& load_gif_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_gif_external(): Specified filename is (null).", + cimglist_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + if (!_load_gif_external(filename,false)) + if (!_load_gif_external(filename,true)) + try { assign(CImg().load_other(filename)); } catch (CImgException&) { assign(); } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_gif_external(): Failed to open file '%s'.", + cimglist_instance,filename); + return *this; + } + + CImgList& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { + char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + std::FILE *file = 0; + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if (use_graphicsmagick) cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png.0",filetmp); + else cimg_snprintf(filetmp2,sizeof(filetmp2),"%s-0.png",filetmp); + if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + } while (file); +#if cimg_OS!=2 + if (use_graphicsmagick) cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s.png\" >/dev/null 2>&1", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); + else cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s.png\" >/dev/null 2>&1", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); +#else + if (use_graphicsmagick) cimg_snprintf(command,sizeof(command),"\"%s convert \"%s\" \"%s.png\"\" >NUL 2>&1", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); + else cimg_snprintf(command,sizeof(command),"\"%s \"%s\" \"%s.png\"\" >NUL 2>&1", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); +#endif + cimg::system(command,0); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode() = 0; + assign(); + + // Try to read a single frame gif. + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png",filetmp); + CImg img; + try { img.load_png(filetmp2); } + catch (CImgException&) { } + if (img) { img.move_to(*this); std::remove(filetmp2); } + else { // Try to read animated gif. + unsigned int i = 0; + for (bool stopflag = false; !stopflag; ++i) { + if (use_graphicsmagick) cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png.%u",filetmp,i); + else cimg_snprintf(filetmp2,sizeof(filetmp2),"%s-%u.png",filetmp,i); + CImg img; + try { img.load_png(filetmp2); } + catch (CImgException&) { stopflag = true; } + if (img) { img.move_to(*this); std::remove(filetmp2); } + } + } + cimg::exception_mode() = omode; + return *this; + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance. + static CImgList get_load_gif_external(const char *const filename) { + return CImgList().load_gif_external(filename); + } + + //! Load a gzipped list, using external tool 'gunzip'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Specified filename is (null).", + cimglist_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filetmp)._system_strescape().data()); + cimg::system(command); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Failed to open file '%s'.", + cimglist_instance, + filename); + + } else cimg::fclose(file); + load(filetmp); + std::remove(filetmp); + return *this; + } + + //! Load a gzipped list, using external tool 'gunzip' \newinstance. + static CImgList get_load_gzip_external(const char *const filename) { + return CImgList().load_gzip_external(filename); + } + + //! Load a 3d object from a .OFF file. + /** + \param filename Filename to read data from. + \param[out] primitives At return, contains the list of 3d object primitives. + \param[out] colors At return, contains the list of 3d object colors. + \return List of 3d object vertices. + **/ + template + CImgList& load_off(const char *const filename, + CImgList& primitives, CImgList& colors) { + return get_load_off(filename,primitives,colors).move_to(*this); + } + + //! Load a 3d object from a .OFF file \newinstance. + template + static CImgList get_load_off(const char *const filename, + CImgList& primitives, CImgList& colors) { + return CImg().load_off(filename,primitives,colors)<'x'; + } + + //! Load images from a TIFF file. + /** + \param filename Filename to read data from. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + **/ + CImgList& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + const unsigned int + nfirst_frame = first_frame::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_instance + "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since file '%s' contains %u image(s).", + cimglist_instance, + nfirst_frame,nlast_frame,nstep_frame,filename,nb_images); + + 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_verbosity>=3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame); + TIFFClose(tif); + } else throw CImgIOException(_cimglist_instance + "load_tiff(): Failed to open file '%s'.", + cimglist_instance, + filename); + return *this; +#endif + } + + //! Load a multi-page TIFF file \newinstance. + static CImgList get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame); + } + + //@} + //---------------------------------- + // + //! \name Data Output + //@{ + //---------------------------------- + + //! Print informations about the list on the standard output. + /** + \param title Label set to the informations displayed. + \param display_stats Tells if image statistics must be computed and displayed. + **/ + const CImgList& print(const char *const title=0, const bool display_stats=true) const { + unsigned int 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 _title[64] = { 0 }; + if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type()); + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_allocated_width, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1)); + else std::fprintf(cimg::output(),".\n"); + + char tmp[16] = { 0 }; + cimglist_for(*this,ll) { + cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); + std::fprintf(cimg::output()," "); + _data[ll].print(tmp,display_stats); + if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output()," ...\n"); } + } + std::fflush(cimg::output()); + return *this; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignmenet. + \note This function displays the list images of the current CImgList instance into an existing CImgDisplay window. + Images of the list are appended in a single temporarly image for visualization purposes. + The function returns immediately. + **/ + const CImgList& display(CImgDisplay &disp, const char axis='x', const float align=0) const { + get_append(axis,align).display(disp); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + \param disp Display window. + \param display_info Tells if image informations are displayed on the standard output. + \param axis Alignment axis for images viewing. + \param align Apending alignment. + \note 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 appended 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. + **/ + const CImgList& display(CImgDisplay &disp, const bool display_info, + const char axis='x', const float align=0, + unsigned int *const XYZ=0) const { + bool is_exit = false; + return _display(disp,0,display_info,axis,align,XYZ,0,true,is_exit); + } + + //! Display the current CImgList instance in a new display window. + /** + \param title Title of the opening display window. + \param display_info Tells if list informations must be written on standard output. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + const CImgList& display(const char *const title=0, const bool display_info=true, + const char axis='x', const float align=0, + unsigned int *const XYZ=0) const { + CImgDisplay disp; + bool is_exit = false; + return _display(disp,title,display_info,axis,align,XYZ,0,true,is_exit); + } + + const CImgList& _display(CImgDisplay &disp, const char *const title, const bool display_info, + const char axis, const float align, unsigned int *const XYZ, + const unsigned int orig, const bool is_first_call, bool &is_exit) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "display(): Empty instance.", + cimglist_instance); + if (!disp) { + if (axis=='x') { + unsigned int sum_width = 0, max_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + sum_width+=w; + if (h>max_height) max_height = h; + } + disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + } else { + unsigned int max_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + sum_height+=h; + } + disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + } + if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else if (title) disp.set_title("%s",title); + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(disp.title()); + disp.show().flush(); + + if (_width==1) { + const unsigned int dw = disp._width, dh = disp._height; + if (!is_first_call) + disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false). + set_title("%s (%ux%ux%ux%u)",dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); + _data[0]._display(disp,0,false,XYZ,!is_first_call); + if (disp.key()) is_exit = true; + disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); + } else { + bool disp_resize = !is_first_call; + while (!disp.is_closed() && !is_exit) { + const CImg s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true); + disp_resize = true; + if (s[0]<0) { // No selections done. + if (disp.button()&2) { disp.flush(); break; } + is_exit = true; + } else if (disp.wheel()) { // Zoom in/out. + const int wheel = disp.wheel(); + disp.set_wheel(); + if (!is_first_call && wheel<0) break; + if (wheel>0 && _width>=4) { + const unsigned int + delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)), + ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta), + ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta); + if ((ind0!=0 || ind1!=_width-1) && ind1 - ind0>=3) get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,XYZ,orig + ind0,false,is_exit); + } + } else if (s[0]!=0 || s[1]!=width()-1) get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,orig+s[0],false,is_exit); + } + } + return *this; + } + + //! Save list into a file. + /** + \param filename Filename to write data to. + \param number Number of digits used when chosen format requires the saving of multiple files. + **/ + const CImgList& save(const char *const filename, const int number=-1) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save(): Specified filename is (null).", + cimglist_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const char *const ext = cimg::split_filename(filename); + char nfilename[1024] = { 0 }; + 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 + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + else 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,"ogg") || + !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); +#ifdef cimg_use_tiff + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); +#endif + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + else { if (_width==1) _data[0].save(fn,-1); else cimglist_for(*this,l) _data[l].save(fn,l); } + return *this; + } + + //! Tell if an image list can be saved as one single file. + /** + \param filename Filename, as a C-string. + \return \c true if the file format supports multiple images, \c false otherwise. + **/ + static bool is_saveable(const char *const filename) { + const char *const ext = cimg::split_filename(filename); + if (!cimg::strcasecmp(ext,"cimgz") || +#ifdef cimg_use_tiff + !cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff") || +#endif + !cimg::strcasecmp(ext,"yuv") || + !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,"ogg") || + !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 true; + return false; + } + + //! Save image sequence as a GIF animated file. + /** + \param filename Filename to write data to. + \param fps Number of desired frames per second. + \param nb_loops Number of loops (\c 0 for infinite looping). + **/ + const CImgList& save_gif_external(const char *const filename, const unsigned int fps=25, const unsigned int nb_loops=0) { + char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + CImgList filenames; + std::FILE *file = 0; + +#ifdef cimg_use_png +#define _cimg_save_gif_ext "png" +#else +#define _cimg_save_gif_ext "ppm" +#endif + + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001." _cimg_save_gif_ext,filetmp); + if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u." _cimg_save_gif_ext,filetmp,l+1); + CImg::string(filetmp2).move_to(filenames); + if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filetmp2); + else _data[l].save(filetmp2); + } + +#if cimg_OS!=2 + cimg_snprintf(command,sizeof(command),"%s -delay 1x%u -loop %u", + cimg::imagemagick_path(),fps,nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,sizeof(command),"\"%s\" >/dev/null 2>&1", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); +#else + cimg_snprintf(command,sizeof(command),"\"%s -delay 1x%u -loop %u", + cimg::imagemagick_path(),fps,nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,sizeof(command),"\"%s\"\" >NUL 2>&1", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); +#endif + CImg _command = filenames>'x'; + cimg_for(_command,p,char) if (!*p) *p = ' '; + _command.back() = 0; + + cimg::system(_command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gif_external(): Failed to save file '%s' with external command 'convert'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for_in(*this,1,filenames._width-1,l) std::remove(filenames[l]); + return *this; + } + + //! Save image sequence, using FFMPEG library. + /** + \param filename Filename to write data to. + \param fps Desired framerate (in frames per seconds) if chosen format supports it. + \param bitrate Desired bitrate (in bits per seconds) if chosen format supports it. + **/ + // This piece of code has been originally written by David. G. Starkweather. + const CImgList& save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_ffmpeg(): Specified filename is (null).", + cimglist_instance); + if (!fps) + throw CImgArgumentException(_cimglist_instance + "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.", + cimglist_instance, + filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) + throw CImgInstanceException(_cimglist_instance + "save_ffmpeg(): Invalid instance dimensions, for file '%s'.", + cimglist_instance, + filename); + +#ifndef cimg_use_ffmpeg + return save_ffmpeg_external(filename,0,fps,bitrate); +#else + avcodec_register_all(); + av_register_all(); + const int + frame_dimx = _data[0].width(), + frame_dimy = _data[0].height(), + frame_dimv = _data[0].spectrum(); + if (frame_dimv!=1 && frame_dimv!=3) + throw CImgInstanceException(_cimglist_instance + "save_ffmpeg(): Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.", + cimglist_instance, + _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename); + + 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; +#if defined(AV_VERSION_INT) +#if LIBAVFORMAT_VERSION_INToformat = fmt; + std::sprintf(oc->filename,"%s",filename); + + // Add video stream. + AVStream *video_str = 0; + if (fmt->video_codec!=CODEC_ID_NONE) { + video_str = avformat_new_stream(oc,NULL); + if (!video_str) { // Failed to allocate stream. + av_free(oc); + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): Failed to allocate FFMPEG structure for video stream, for file '%s'.", + cimglist_instance, + filename); + } + } else { // No codec identified. + av_free(oc); + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): Failed to identify proper codec, for file '%s'.", + cimglist_instance, + filename); + } + + AVCodecContext *c = video_str->codec; + c->codec_id = fmt->video_codec; + c->codec_type = AVMEDIA_TYPE_VIDEO; + c->bit_rate = 1024*bitrate; + 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; + + // Open codecs and alloc buffers. + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { // Failed to find codec. + av_free(oc); + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): No valid codec found for file '%s'.", + cimglist_instance, + filename); + } + if (avcodec_open2(c,codec,NULL)<0) // Failed to open codec. + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): Failed to open codec for file '%s'.", + cimglist_instance, + 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_instance + "save_ffmpeg(): Failed to allocate memory for file '%s'.", + cimglist_instance, + 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_instance + "save_ffmpeg(): Failed to allocate memory for file '%s'.", + cimglist_instance, + 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_instance + "save_ffmpeg(): Failed to allocate memory for file '%s'.", + cimglist_instance, + 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_instance + "save_ffmpeg(): Failed to allocate memory for file '%s'.", + cimglist_instance, + 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 (avio_open(&oc->pb,filename,AVIO_FLAG_WRITE)<0) + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): Failed to open file '%s'.", + cimglist_instance, + filename); + } + + if (avformat_write_header(oc,NULL)<0) + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): Failed to write header in file '%s'.", + cimglist_instance, + filename); + + 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_instance + "save_ffmpeg(): Failed to get conversion context for file '%s'.", + cimglist_instance, + 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_instance + "save_ffmpeg(): Failed to allocate memory, for file '%s'.", + cimglist_instance, + filename); + } + + // Loop through each desired image in list. + cimglist_for(*this,i) { + CImg currentIm = _data[i], red, green, blue, gray; + if (src_pxl_fmt==PIX_FMT_RGB24) { + red = currentIm.get_shared_channel(0); + green = currentIm.get_shared_channel(1); + blue = currentIm.get_shared_channel(2); + cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format. + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y); + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y); + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y); + } + } else { + gray = currentIm.get_shared_channel(0); + cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y); + } + if (!video_str) break; + if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break; + + AVPacket pkt; + int got_packet; + av_init_packet(&pkt); + out_size = avcodec_encode_video2(c,&pkt,picture,&got_packet); + if (got_packet) { + pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base); + if (c->coded_frame->key_frame) pkt.flags|=AV_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_instance + "save_ffmpeg(): Failed to write trailer for file '%s'.", + cimglist_instance, + filename); + + av_freep(&oc->streams[0]->codec); + av_freep(&oc->streams[0]); + if (!(fmt->flags&AVFMT_NOFILE)) { + /*if (url_fclose(oc->pb)<0) + throw CImgIOException(_cimglist_instance + "save_ffmpeg(): File '%s', failed to close file.", + cimglist_instance, + filename); + */ + } + av_free(oc); + av_free(video_outbuf); +#endif + return *this; + } + + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if ((*this)[0].width()%2 || (*this)[0].height()%2) + throw CImgInstanceException(_cimglist_instance + "save_yuv(): Invalid odd instance dimensions (%u,%u) for file '%s'.", + cimglist_instance, + (*this)[0].width(),(*this)[0].height(), + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + CImg YCbCr((*this)[l]); + if (is_rgb) YCbCr.RGBtoYCbCr(); + cimg::fwrite(YCbCr._data,(unsigned long)YCbCr._width*YCbCr._height,nfile); + cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1), + (unsigned long)YCbCr._width*YCbCr._height/2,nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list as a YUV image sequence file. + /** + \param filename Filename to write data to. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(const char *const filename=0, const bool is_rgb=true) const { + return _save_yuv(0,filename,is_rgb); + } + + //! Save image sequence into a YUV file. + /** + \param file File to write data to. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(std::FILE *const file, const bool is_rgb=true) const { + return _save_yuv(file,0,is_rgb); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); +#endif + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype); + else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); + cimglist_for(*this,l) { + const CImg& img = _data[l]; + std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + const unsigned long siz = sizeof(T)*ref.size(); + unsigned long csiz = siz + siz/100 + 16; + Bytef *const cbuf = new Bytef[csiz]; + if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) + cimg::warn(_cimglist_instance + "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); + else { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + delete[] cbuf; + failed_to_compress = false; + } +#endif + } + if (failed_to_compress) { // Write in a non-compressed way. + std::fputc('\n',nfile); + cimg::fwrite(ref._data,ref.size(),nfile); + } + } else std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param filename Filename to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); + } + + //! Save list into a .cimg file. + /** + \param file File to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(std::FILE *file, const bool is_compressed=false) const { + return _save_cimg(file,0,is_compressed); + } + + const CImgList& _save_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 c0) const { +#define _cimg_save_cimg_case(Ts,Tss) \ + if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l0) { \ + if (l=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const CImg& img = (*this)[l - n0]; \ + const T *ptrs = img._data; \ + const unsigned int \ + x1 = x0 + img._width - 1, \ + y1 = y0 + img._height - 1, \ + z1 = z0 + img._depth - 1, \ + c1 = c0 + img._spectrum - 1, \ + nx1 = x1>=W?W-1:x1, \ + ny1 = y1>=H?H-1:y1, \ + nz1 = z1>=D?D-1:z1, \ + nc1 = c1>=C?C-1:c1; \ + CImg raw(1+nx1-x0); \ + const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ + if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v = 1 + nc1 - c0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) 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) std::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + ny1 - y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) 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) std::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ + if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ + if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ + if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_cimg(): Empty instance, for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); + bool saved = false, endian = cimg::endianness(); + char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; + unsigned int j, err, N, W, H, D, C; + int i; + j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = 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_instance + "save_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + 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+_width); + _cimg_save_cimg_case("bool",bool); + _cimg_save_cimg_case("unsigned_char",unsigned char); + _cimg_save_cimg_case("uchar",unsigned char); + _cimg_save_cimg_case("char",char); + _cimg_save_cimg_case("unsigned_short",unsigned short); + _cimg_save_cimg_case("ushort",unsigned short); + _cimg_save_cimg_case("short",short); + _cimg_save_cimg_case("unsigned_int",unsigned int); + _cimg_save_cimg_case("uint",unsigned int); + _cimg_save_cimg_case("int",int); + _cimg_save_cimg_case("unsigned_long",unsigned long); + _cimg_save_cimg_case("ulong",unsigned long); + _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_instance + "save_cimg(): Unsupported data type '%s' for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)",str_pixeltype); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param filename Filename to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(0,filename,n0,x0,y0,z0,c0); + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param file File to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(file,0,n0,x0,y0,z0,c0); + } + + static void _save_empty_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 dc) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned long siz = (unsigned long)dx*dy*dz*dc*sizeof(T); + std::fprintf(nfile,"%u %s\n",nb,pixel_type()); + for (unsigned int i=nb; i; --i) { + std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc); + for (unsigned long off=siz; off; --off) std::fputc(0,nfile); + } + if (!file) cimg::fclose(nfile); + } + + //! Save empty (non-compressed) .cimg file with specified dimensions. + /** + \param filename Filename to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); + } + + //! Save empty .cimg file with specified dimensions. + /** + \param file File to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); + } + + //! Save list as a TIFF file. + /** + \param filename Filename to write data to. + \param compression_type Compression mode used to write data. + **/ + const CImgList& save_tiff(const char *const filename, const unsigned int compression_type=0) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_tiff(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_tiff + if (_width==1) _data[0].save_tiff(filename,compression_type); + else cimglist_for(*this,l) { + char nfilename[1024] = { 0 }; + cimg::number_filename(filename,l,6,nfilename); + _data[l].save_tiff(nfilename,compression_type); + } +#else + TIFF *tif = TIFFOpen(filename,"w"); + if (tif) { + for (unsigned int dir = 0, l = 0; l<_width; ++l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._depth==1) img._save_tiff(tif,dir++,compression_type); + else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression_type); + } + } + TIFFClose(tif); + } else + throw CImgIOException(_cimglist_instance + "save_tiff(): Failed to open stream for file '%s'.", + cimglist_instance, + filename); +#endif + return *this; + } + + + //! Save list as a gzipped file, using external tool 'gzip'. + /** + \param filename Filename to write data to. + **/ + const CImgList& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Specified filename is (null).", + cimglist_instance); + + char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + } while (file); + + if (is_saveable(body)) { + save(filetmp); + cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filetmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimglist_instance, + filename); + else cimg::fclose(file); + std::remove(filetmp); + } else { + char nfilename[1024] = { 0 }; + cimglist_for(*this,l) { + cimg::number_filename(body,l,6,nfilename); + if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext); + _data[l].save_gzip_external(nfilename); + } + } + return *this; + } + + //! Save image sequence, using the external tool 'ffmpeg'. + /** + \param filename Filename to write data to. + \param codec Type of compression. + \param fps Number of frames per second. + \param bitrate Output bitrate + **/ + const CImgList& save_ffmpeg_external(const char *const filename, const char *const codec=0, + const unsigned int fps=25, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_ffmpeg_external(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + const char + *const ext = cimg::split_filename(filename), + *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video"; + + char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + CImgList filenames; + std::FILE *file = 0; + cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) + throw CImgInstanceException(_cimglist_instance + "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.", + cimglist_instance, + filename); + do { + cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp); + if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1); + CImg::string(filetmp2).move_to(filenames); + if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2); + else _data[l].save_pnm(filetmp2); + } +#if cimg_OS!=2 + cimg_snprintf(command,sizeof(command),"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1", + cimg::ffmpeg_path(), + CImg::string(filetmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); +#else + cimg_snprintf(command,sizeof(command),"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1", + cimg::ffmpeg_path(), + CImg::string(filetmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); +#endif + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for(*this,l) std::remove(filenames[l]); + return *this; + } + + //@} + //---------------------------------- + // + //! \name Others + //@{ + //---------------------------------- + + //! Crop font along the X-axis. + /** + **/ + CImgList& crop_font() { + return get_crop_font().move_to(*this); + } + + //! Crop font along the X-axis \newinstance. + /** + **/ + CImgList get_crop_font() const { + CImgList res; + cimglist_for(*this,l) { + const CImg& letter = (*this)[l]; + int xmin = letter._width, xmax = 0; + cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax = x; } + if (xmin>xmax) CImg(letter._width,letter._height,1,letter._spectrum,0).move_to(res); + else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res); + } + res[' '].resize(res['f']._width,-100,-100,-100,0); + if (' '+256& font(const unsigned int font_height, const bool is_variable_width=true) { + +#define _cimg_font(sx,sy) \ + if (!is_variable_width && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \ + if (is_variable_width && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \ + if (font_height==sy) return is_variable_width?vfont:font; \ + if (is_variable_width) { \ + if (cvfont && font_height==cvfont[0]._height) return cvfont; \ + cvfont = vfont; \ + cimglist_for(cvfont,l) \ + cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \ + cvfont[0]._height>font_height?2:5); \ + return cvfont; \ + } else { \ + if (cfont && font_height==cfont[0]._height) return cfont; \ + cfont = font; \ + cimglist_for(cfont,l) \ + cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \ + cfont[0]._height>font_height?2:5); \ + return cfont; \ + } \ + + static CImgList font, vfont, cfont, cvfont; + if (!font_height) return CImgList::empty(); + if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13 + if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24 + if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32 + _cimg_font(29,57); // [33,+inf] -> ref 57 + } + + static CImgList _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool is_variable_width) { + CImgList res(256,w,h,1,1); + const unsigned int *ptr = font; + unsigned int m = 0, val = 0; + for (unsigned int y = 0; y>=1; if (!m) { m = 0x80000000; val = *(ptr++); } + CImg& img = res[x/w]; + unsigned int xm = x%w; + img(xm,y) = (T)((val&m)?1:0); + } + if (is_variable_width) res.crop_font(); + return res.insert(res); + } + + //! Compute a 1d Fast Fourier Transform, along specified axis. + /** + \param axis Axis along which the Fourier transform is computed. + \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. + **/ + CImgList& FFT(const char axis, const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + + CImg::FFT(_data[0],_data[1],axis,invert); + return *this; + } + + //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this,false).FFT(axis,invert); + } + + //! Compute a n-d Fast Fourier Transform. + /** + \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. + **/ + CImgList& FFT(const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + + CImg::FFT(_data[0],_data[1],invert); + return *this; + } + + //! Compute a n-d Fast Fourier Transform \newinstance. + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this,false).FFT(invert); + } + + //! Reverse primitives orientations of a 3d object. + /** + **/ + CImgList& reverse_object3d() { + cimglist_for(*this,l) { + CImg& p = _data[l]; + switch (p.size()) { + case 2: case 3: cimg::swap(p[0],p[1]); break; + case 6: cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break; + case 9: cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break; + case 4: cimg::swap(p[0],p[1],p[2],p[3]); break; + case 12: cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break; + } + } + return *this; + } + + //! Reverse primitives orientations of a 3d object \newinstance. + CImgList get_reverse_object3d() const { + return (+*this).reverse_object3d(); + } + + //@} + }; // struct CImgList { ... + + /* + #--------------------------------------------- + # + # Completion of previously declared functions + # + #---------------------------------------------- + */ + +namespace cimg { + + //! Display a simple dialog box, and wait for the user's response. + /** + \param title Title of the dialog window. + \param msg Main message displayed inside the dialog window. + \param button1_label Label of the 1st button. + \param button2_label Label of the 2nd button (\c 0 to hide button). + \param button3_label Label of the 3rd button (\c 0 to hide button). + \param button4_label Label of the 4th button (\c 0 to hide button). + \param button5_label Label of the 5th button (\c 0 to hide button). + \param button6_label Label of the 6th button (\c 0 to hide button). + \param logo Image logo displayed at the left of the main message. + \param is_centered Tells if the dialog window must be centered on the screen. + \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user. + \note + - Up to 6 buttons can be defined in the dialog window. + - The function returns when a user clicked one of the button or closed the dialog window. + - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box. At least one button must be specified. + **/ + template + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const CImg& logo, const bool is_centered = false) { +#if cimg_display==0 + cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,logo._data,is_centered); + throw CImgIOException("cimg::dialog(): No display available."); +#else + const unsigned char + black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_label) { CImg().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons); + if (button2_label) { CImg().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons); + if (button3_label) { CImg().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons); + if (button4_label) { CImg().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons); + if (button5_label) { CImg().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons); + if (button6_label) { CImg().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons); + }}}}}} + if (!buttons._width) + throw CImgArgumentException("cimg::dialog(): No buttons have been defined."); + cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3); + + unsigned int bw = 0, bh = 0; + cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); } + bw+=8; bh+=8; + if (bw<64) bw = 64; + if (bw>128) bw = 128; + if (bh<24) bh = 24; + if (bh>48) bh = 48; + + CImg button(bw,bh,1,3); + button.draw_rectangle(0,0,bw-1,bh-1,gray); + button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white); + button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black); + button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2); + CImg sbutton(bw,bh,1,3); + sbutton.draw_rectangle(0,0,bw-1,bh-1,gray); + sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black); + sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black); + sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white); + sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black); + sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2); + sbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false); + sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false); + CImg cbutton(bw,bh,1,3); + cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray); + cbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false); + cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false); + + cimglist_for(buttons,ll) { + CImg(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]). + move_to(cbuttons); + CImg(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]). + move_to(sbuttons); + CImg(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]). + move_to(buttons[ll]); + } + + CImg canvas; + if (msg) CImg().draw_text(0,0,"%s",black,gray,1,13,msg).resize(-100,-100,1,3).move_to(canvas); + const unsigned int + bwall = (buttons._width-1)*(12+bw) + bw, + w = cimg::max(196U,36+logo._width+canvas._width,24+bwall), + h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh), + lx = 12 + (canvas._data?0:((w-24-logo._width)/2)), + ly = (h-12-bh-logo._height)/2, + tx = lx+logo._width+12, + ty = (h-12-bh-canvas._height)/2, + bx = (w-bwall)/2, + by = h-12-bh; + + if (canvas._data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black). + draw_image(tx,ty,canvas); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black); + if (logo._data) canvas.draw_image(lx,ly,logo); + + unsigned int xbuttons[6] = { 0 }; + 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,is_centered?true:false); + if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, + (CImgDisplay::screen_height() - disp.height())/2); + bool stopflag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed() && !stopflag) { + if (refresh) { + if (clicked>=0) CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + else { + if (selected>=0) CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized()) disp.resize(disp,false); + + 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._width; break; + case cimg::keyARROWLEFT : + case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break; + } + disp.set_key(); + if (selected!=oselected) refresh = true; + } + } + if (!disp) selected = -1; + return selected; +#endif + } + + //! Display a simple dialog box, and wait for the user's response \specialization. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, const char *const button3_label, + const char *const button4_label, const char *const button5_label, const char *const button6_label, + const bool is_centered) { + return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + CImg::_logo40x38(),is_centered); + } + + //! Evaluate math expression. + /** + \param expression C-string describing the formula to evaluate. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \return Result of the formula evaluation. + \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. + \par Example + \code + const double + res1 = cimg::eval("cos(x)^2+sin(y)^2",2,2), // will return '1'. + res2 = cimg::eval(0,1,1); // will return '1' too. + \endcode + **/ + inline double eval(const char *const expression, const double x, const double y, const double z, const double c) { + static const CImg empty; + return empty.eval(expression,x,y,z,c); + } + + // End of cimg:: namespace +} + + // End of cimg_library:: namespace +} + +//! Short alias name. +namespace cil = cimg_library_suffixed; + +#ifdef _cimg_redefine_False +#define False 0 +#endif +#ifdef _cimg_redefine_True +#define True 1 +#endif +#ifdef _cimg_redefine_None +#define None 0 +#endif +#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 +#ifdef _cimg_redefine_PI +#define PI 3.141592653589793238462643383 +#endif + +#endif +// Local Variables: +// mode: c++ +// End: diff --git a/FTD2XX.h b/FTD2XX.h new file mode 100644 index 0000000..6bf632b --- /dev/null +++ b/FTD2XX.h @@ -0,0 +1,1067 @@ +/*++ + +Copyright © 2001-2011 Future Technology Devices International Limited + +THIS SOFTWARE IS PROVIDED BY FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FTDI DRIVERS MAY BE USED ONLY IN CONJUNCTION WITH PRODUCTS BASED ON FTDI PARTS. + +FTDI DRIVERS MAY BE DISTRIBUTED IN ANY FORM AS LONG AS LICENSE INFORMATION IS NOT MODIFIED. + +IF A CUSTOM VENDOR ID AND/OR PRODUCT ID OR DESCRIPTION STRING ARE USED, IT IS THE +RESPONSIBILITY OF THE PRODUCT MANUFACTURER TO MAINTAIN ANY CHANGES AND SUBSEQUENT WHQL +RE-CERTIFICATION AS A RESULT OF MAKING THESE CHANGES. + + +Module Name: + +ftd2xx.h + +Abstract: + +Native USB device driver for FTDI FT232x, FT245x, FT2232x and FT4232x devices +FTD2XX library definitions + +Environment: + +kernel & user mode + + +--*/ + + +#ifndef FTD2XX_H +#define FTD2XX_H + +// The following ifdef block is the standard way of creating macros +// which make exporting from a DLL simpler. All files within this DLL +// are compiled with the FTD2XX_EXPORTS symbol defined on the command line. +// This symbol should not be defined on any project that uses this DLL. +// This way any other project whose source files include this file see +// FTD2XX_API functions as being imported from a DLL, whereas this DLL +// sees symbols defined with this macro as being exported. + +#ifdef FTD2XX_EXPORTS +#define FTD2XX_API __declspec(dllexport) +#else +#define FTD2XX_API __declspec(dllimport) +#endif + + +typedef PVOID FT_HANDLE; +typedef ULONG FT_STATUS; + +// +// Device status +// +enum { + FT_OK, + FT_INVALID_HANDLE, + FT_DEVICE_NOT_FOUND, + FT_DEVICE_NOT_OPENED, + FT_IO_ERROR, + FT_INSUFFICIENT_RESOURCES, + FT_INVALID_PARAMETER, + FT_INVALID_BAUD_RATE, + + FT_DEVICE_NOT_OPENED_FOR_ERASE, + FT_DEVICE_NOT_OPENED_FOR_WRITE, + FT_FAILED_TO_WRITE_DEVICE, + FT_EEPROM_READ_FAILED, + FT_EEPROM_WRITE_FAILED, + FT_EEPROM_ERASE_FAILED, + FT_EEPROM_NOT_PRESENT, + FT_EEPROM_NOT_PROGRAMMED, + FT_INVALID_ARGS, + FT_NOT_SUPPORTED, + FT_OTHER_ERROR, + FT_DEVICE_LIST_NOT_READY, +}; + + +#define FT_SUCCESS(status) ((status) == FT_OK) + +// +// FT_OpenEx Flags +// + +#define FT_OPEN_BY_SERIAL_NUMBER 1 +#define FT_OPEN_BY_DESCRIPTION 2 +#define FT_OPEN_BY_LOCATION 4 + +// +// FT_ListDevices Flags (used in conjunction with FT_OpenEx Flags +// + +#define FT_LIST_NUMBER_ONLY 0x80000000 +#define FT_LIST_BY_INDEX 0x40000000 +#define FT_LIST_ALL 0x20000000 + +#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL) + +// +// Baud Rates +// + +#define FT_BAUD_300 300 +#define FT_BAUD_600 600 +#define FT_BAUD_1200 1200 +#define FT_BAUD_2400 2400 +#define FT_BAUD_4800 4800 +#define FT_BAUD_9600 9600 +#define FT_BAUD_14400 14400 +#define FT_BAUD_19200 19200 +#define FT_BAUD_38400 38400 +#define FT_BAUD_57600 57600 +#define FT_BAUD_115200 115200 +#define FT_BAUD_230400 230400 +#define FT_BAUD_460800 460800 +#define FT_BAUD_921600 921600 + +// +// Word Lengths +// + +#define FT_BITS_8 (UCHAR) 8 +#define FT_BITS_7 (UCHAR) 7 + +// +// Stop Bits +// + +#define FT_STOP_BITS_1 (UCHAR) 0 +#define FT_STOP_BITS_2 (UCHAR) 2 + +// +// Parity +// + +#define FT_PARITY_NONE (UCHAR) 0 +#define FT_PARITY_ODD (UCHAR) 1 +#define FT_PARITY_EVEN (UCHAR) 2 +#define FT_PARITY_MARK (UCHAR) 3 +#define FT_PARITY_SPACE (UCHAR) 4 + +// +// Flow Control +// + +#define FT_FLOW_NONE 0x0000 +#define FT_FLOW_RTS_CTS 0x0100 +#define FT_FLOW_DTR_DSR 0x0200 +#define FT_FLOW_XON_XOFF 0x0400 + +// +// Purge rx and tx buffers +// +#define FT_PURGE_RX 1 +#define FT_PURGE_TX 2 + +// +// Events +// + +typedef void (*PFT_EVENT_HANDLER)(DWORD,DWORD); + +#define FT_EVENT_RXCHAR 1 +#define FT_EVENT_MODEM_STATUS 2 +#define FT_EVENT_LINE_STATUS 4 + +// +// Timeouts +// + +#define FT_DEFAULT_RX_TIMEOUT 300 +#define FT_DEFAULT_TX_TIMEOUT 300 + +// +// Device types +// + +typedef ULONG FT_DEVICE; + +enum { + FT_DEVICE_BM, + FT_DEVICE_AM, + FT_DEVICE_100AX, + FT_DEVICE_UNKNOWN, + FT_DEVICE_2232C, + FT_DEVICE_232R, + FT_DEVICE_2232H, + FT_DEVICE_4232H, + FT_DEVICE_232H +}; + +// +// Bit Modes +// + +#define FT_BITMODE_RESET 0x00 +#define FT_BITMODE_ASYNC_BITBANG 0x01 +#define FT_BITMODE_MPSSE 0x02 +#define FT_BITMODE_SYNC_BITBANG 0x04 +#define FT_BITMODE_MCU_HOST 0x08 +#define FT_BITMODE_FAST_SERIAL 0x10 +#define FT_BITMODE_CBUS_BITBANG 0x20 +#define FT_BITMODE_SYNC_FIFO 0x40 + +// +// FT232R CBUS Options EEPROM values +// + +#define FT_232R_CBUS_TXDEN 0x00 // Tx Data Enable +#define FT_232R_CBUS_PWRON 0x01 // Power On +#define FT_232R_CBUS_RXLED 0x02 // Rx LED +#define FT_232R_CBUS_TXLED 0x03 // Tx LED +#define FT_232R_CBUS_TXRXLED 0x04 // Tx and Rx LED +#define FT_232R_CBUS_SLEEP 0x05 // Sleep +#define FT_232R_CBUS_CLK48 0x06 // 48MHz clock +#define FT_232R_CBUS_CLK24 0x07 // 24MHz clock +#define FT_232R_CBUS_CLK12 0x08 // 12MHz clock +#define FT_232R_CBUS_CLK6 0x09 // 6MHz clock +#define FT_232R_CBUS_IOMODE 0x0A // IO Mode for CBUS bit-bang +#define FT_232R_CBUS_BITBANG_WR 0x0B // Bit-bang write strobe +#define FT_232R_CBUS_BITBANG_RD 0x0C // Bit-bang read strobe + +// +// FT232H CBUS Options EEPROM values +// + +#define FT_232H_CBUS_TRISTATE 0x00 // Tristate +#define FT_232H_CBUS_RXLED 0x01 // Rx LED +#define FT_232H_CBUS_TXLED 0x02 // Tx LED +#define FT_232H_CBUS_TXRXLED 0x03 // Tx and Rx LED +#define FT_232H_CBUS_PWREN 0x04 // Power Enable +#define FT_232H_CBUS_SLEEP 0x05 // Sleep +#define FT_232H_CBUS_DRIVE_0 0x06 // Drive pin to logic 0 +#define FT_232H_CBUS_DRIVE_1 0x07 // Drive pin to logic 1 +#define FT_232H_CBUS_IOMODE 0x08 // IO Mode for CBUS bit-bang +#define FT_232H_CBUS_TXDEN 0x09 // Tx Data Enable +#define FT_232H_CBUS_CLK30 0x0A // 30MHz clock +#define FT_232H_CBUS_CLK15 0x0B // 15MHz clock +#define FT_232H_CBUS_CLK7_5 0x0C // 7.5MHz clock + + +#ifdef __cplusplus +extern "C" { +#endif + + + FTD2XX_API + FT_STATUS WINAPI FT_Open( + int deviceNumber, + FT_HANDLE *pHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_OpenEx( + PVOID pArg1, + DWORD Flags, + FT_HANDLE *pHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ListDevices( + PVOID pArg1, + PVOID pArg2, + DWORD Flags + ); + + FTD2XX_API + FT_STATUS WINAPI FT_Close( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_Read( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD dwBytesToRead, + LPDWORD lpBytesReturned + ); + + FTD2XX_API + FT_STATUS WINAPI FT_Write( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD dwBytesToWrite, + LPDWORD lpBytesWritten + ); + + FTD2XX_API + FT_STATUS WINAPI FT_IoCtl( + FT_HANDLE ftHandle, + DWORD dwIoControlCode, + LPVOID lpInBuf, + DWORD nInBufSize, + LPVOID lpOutBuf, + DWORD nOutBufSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetBaudRate( + FT_HANDLE ftHandle, + ULONG BaudRate + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetDivisor( + FT_HANDLE ftHandle, + USHORT Divisor + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetDataCharacteristics( + FT_HANDLE ftHandle, + UCHAR WordLength, + UCHAR StopBits, + UCHAR Parity + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetFlowControl( + FT_HANDLE ftHandle, + USHORT FlowControl, + UCHAR XonChar, + UCHAR XoffChar + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ResetDevice( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetDtr( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ClrDtr( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetRts( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ClrRts( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetModemStatus( + FT_HANDLE ftHandle, + ULONG *pModemStatus + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetChars( + FT_HANDLE ftHandle, + UCHAR EventChar, + UCHAR EventCharEnabled, + UCHAR ErrorChar, + UCHAR ErrorCharEnabled + ); + + FTD2XX_API + FT_STATUS WINAPI FT_Purge( + FT_HANDLE ftHandle, + ULONG Mask + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetTimeouts( + FT_HANDLE ftHandle, + ULONG ReadTimeout, + ULONG WriteTimeout + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetQueueStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetEventNotification( + FT_HANDLE ftHandle, + DWORD Mask, + PVOID Param + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes, + DWORD *dwTxBytes, + DWORD *dwEventDWord + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetBreakOn( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetBreakOff( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetWaitMask( + FT_HANDLE ftHandle, + DWORD Mask + ); + + FTD2XX_API + FT_STATUS WINAPI FT_WaitOnMask( + FT_HANDLE ftHandle, + DWORD *Mask + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetEventStatus( + FT_HANDLE ftHandle, + DWORD *dwEventDWord + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ReadEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + LPWORD lpwValue + ); + + FTD2XX_API + FT_STATUS WINAPI FT_WriteEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + WORD wValue + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EraseEE( + FT_HANDLE ftHandle + ); + + // + // structure to hold program data for FT_Program function + // + typedef struct ft_program_data { + + DWORD Signature1; // Header - must be 0x00000000 + DWORD Signature2; // Header - must be 0xffffffff + DWORD Version; // Header - FT_PROGRAM_DATA version + // 0 = original + // 1 = FT2232C extensions + // 2 = FT232R extensions + // 3 = FT2232H extensions + // 4 = FT4232H extensions + // 5 = FT232H extensions + + WORD VendorId; // 0x0403 + WORD ProductId; // 0x6001 + char *Manufacturer; // "FTDI" + char *ManufacturerId; // "FT" + char *Description; // "USB HS Serial Converter" + char *SerialNumber; // "FT000001" if fixed, or NULL + WORD MaxPower; // 0 < MaxPower <= 500 + WORD PnP; // 0 = disabled, 1 = enabled + WORD SelfPowered; // 0 = bus powered, 1 = self powered + WORD RemoteWakeup; // 0 = not capable, 1 = capable + // + // Rev4 (FT232B) extensions + // + UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise + UCHAR IsoIn; // non-zero if in endpoint is isochronous + UCHAR IsoOut; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable; // non-zero if pull down enabled + UCHAR SerNumEnable; // non-zero if serial number to be used + UCHAR USBVersionEnable; // non-zero if chip uses USBVersion + WORD USBVersion; // BCD (0x0200 => USB2) + // + // Rev 5 (FT2232) extensions + // + UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise + UCHAR IsoInA; // non-zero if in endpoint is isochronous + UCHAR IsoInB; // non-zero if in endpoint is isochronous + UCHAR IsoOutA; // non-zero if out endpoint is isochronous + UCHAR IsoOutB; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable5; // non-zero if pull down enabled + UCHAR SerNumEnable5; // non-zero if serial number to be used + UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion + WORD USBVersion5; // BCD (0x0200 => USB2) + UCHAR AIsHighCurrent; // non-zero if interface is high current + UCHAR BIsHighCurrent; // non-zero if interface is high current + UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer; // non-zero if interface is Fast serial + UCHAR AIsVCP; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer; // non-zero if interface is Fast serial + UCHAR BIsVCP; // non-zero if interface is to use VCP drivers + // + // Rev 6 (FT232R) extensions + // + UCHAR UseExtOsc; // Use External Oscillator + UCHAR HighDriveIOs; // High Drive I/Os + UCHAR EndpointSize; // Endpoint size + UCHAR PullDownEnableR; // non-zero if pull down enabled + UCHAR SerNumEnableR; // non-zero if serial number to be used + UCHAR InvertTXD; // non-zero if invert TXD + UCHAR InvertRXD; // non-zero if invert RXD + UCHAR InvertRTS; // non-zero if invert RTS + UCHAR InvertCTS; // non-zero if invert CTS + UCHAR InvertDTR; // non-zero if invert DTR + UCHAR InvertDSR; // non-zero if invert DSR + UCHAR InvertDCD; // non-zero if invert DCD + UCHAR InvertRI; // non-zero if invert RI + UCHAR Cbus0; // Cbus Mux control + UCHAR Cbus1; // Cbus Mux control + UCHAR Cbus2; // Cbus Mux control + UCHAR Cbus3; // Cbus Mux control + UCHAR Cbus4; // Cbus Mux control + UCHAR RIsD2XX; // non-zero if using D2XX driver + // + // Rev 7 (FT2232H) Extensions + // + UCHAR PullDownEnable7; // non-zero if pull down enabled + UCHAR SerNumEnable7; // non-zero if serial number to be used + UCHAR ALSlowSlew; // non-zero if AL pins have slow slew + UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR AHSlowSlew; // non-zero if AH pins have slow slew + UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BLSlowSlew; // non-zero if BL pins have slow slew + UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BHSlowSlew; // non-zero if BH pins have slow slew + UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial + UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial + UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs + // + // Rev 8 (FT4232H) Extensions + // + UCHAR PullDownEnable8; // non-zero if pull down enabled + UCHAR SerNumEnable8; // non-zero if serial number to be used + UCHAR ASlowSlew; // non-zero if AL pins have slow slew + UCHAR ASchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BSlowSlew; // non-zero if AH pins have slow slew + UCHAR BSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR CSlowSlew; // non-zero if BL pins have slow slew + UCHAR CSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR DSlowSlew; // non-zero if BH pins have slow slew + UCHAR DSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN + UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN + UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN + UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN + UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers + // + // Rev 9 (FT232H) Extensions + // + UCHAR PullDownEnableH; // non-zero if pull down enabled + UCHAR SerNumEnableH; // non-zero if serial number to be used + UCHAR ACSlowSlewH; // non-zero if AC pins have slow slew + UCHAR ACSchmittInputH; // non-zero if AC pins are Schmitt input + UCHAR ACDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR ADSlowSlewH; // non-zero if AD pins have slow slew + UCHAR ADSchmittInputH; // non-zero if AD pins are Schmitt input + UCHAR ADDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR Cbus0H; // Cbus Mux control + UCHAR Cbus1H; // Cbus Mux control + UCHAR Cbus2H; // Cbus Mux control + UCHAR Cbus3H; // Cbus Mux control + UCHAR Cbus4H; // Cbus Mux control + UCHAR Cbus5H; // Cbus Mux control + UCHAR Cbus6H; // Cbus Mux control + UCHAR Cbus7H; // Cbus Mux control + UCHAR Cbus8H; // Cbus Mux control + UCHAR Cbus9H; // Cbus Mux control + UCHAR IsFifoH; // non-zero if interface is 245 FIFO + UCHAR IsFifoTarH; // non-zero if interface is 245 FIFO CPU target + UCHAR IsFastSerH; // non-zero if interface is Fast serial + UCHAR IsFT1248H; // non-zero if interface is FT1248 + UCHAR FT1248CpolH; // FT1248 clock polarity - clock idle high (1) or clock idle low (0) + UCHAR FT1248LsbH; // FT1248 data is LSB (1) or MSB (0) + UCHAR FT1248FlowControlH; // FT1248 flow control enable + UCHAR IsVCPH; // non-zero if interface is to use VCP drivers + UCHAR PowerSaveEnableH; // non-zero if using ACBUS7 to save power for self-powered designs + + } FT_PROGRAM_DATA, *PFT_PROGRAM_DATA; + + FTD2XX_API + FT_STATUS WINAPI FT_EE_Program( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_ProgramEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_Read( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_ReadEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_UASize( + FT_HANDLE ftHandle, + LPDWORD lpdwSize + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_UAWrite( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_UARead( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen, + LPDWORD lpdwBytesRead + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetLatencyTimer( + FT_HANDLE ftHandle, + UCHAR ucLatency + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetLatencyTimer( + FT_HANDLE ftHandle, + PUCHAR pucLatency + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetBitMode( + FT_HANDLE ftHandle, + UCHAR ucMask, + UCHAR ucEnable + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetBitMode( + FT_HANDLE ftHandle, + PUCHAR pucMode + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetUSBParameters( + FT_HANDLE ftHandle, + ULONG ulInTransferSize, + ULONG ulOutTransferSize + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetDeadmanTimeout( + FT_HANDLE ftHandle, + ULONG ulDeadmanTimeout + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetDeviceInfo( + FT_HANDLE ftHandle, + FT_DEVICE *lpftDevice, + LPDWORD lpdwID, + PCHAR SerialNumber, + PCHAR Description, + LPVOID Dummy + ); + + FTD2XX_API + FT_STATUS WINAPI FT_StopInTask( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_RestartInTask( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_SetResetPipeRetryCount( + FT_HANDLE ftHandle, + DWORD dwCount + ); + + FTD2XX_API + FT_STATUS WINAPI FT_ResetPort( + FT_HANDLE ftHandle + ); + + FTD2XX_API + FT_STATUS WINAPI FT_CyclePort( + FT_HANDLE ftHandle + ); + + + // + // Win32-type functions + // + + FTD2XX_API + FT_HANDLE WINAPI FT_W32_CreateFile( + LPCTSTR lpszName, + DWORD dwAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreate, + DWORD dwAttrsAndFlags, + HANDLE hTemplate + ); + + FTD2XX_API + BOOL WINAPI FT_W32_CloseHandle( + FT_HANDLE ftHandle + ); + + FTD2XX_API + BOOL WINAPI FT_W32_ReadFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + + FTD2XX_API + BOOL WINAPI FT_W32_WriteFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesWritten, + LPOVERLAPPED lpOverlapped + ); + + FTD2XX_API + DWORD WINAPI FT_W32_GetLastError( + FT_HANDLE ftHandle + ); + + FTD2XX_API + BOOL WINAPI FT_W32_GetOverlappedResult( + FT_HANDLE ftHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpdwBytesTransferred, + BOOL bWait + ); + + FTD2XX_API + BOOL WINAPI FT_W32_CancelIo( + FT_HANDLE ftHandle + ); + + + // + // Win32 COMM API type functions + // + typedef struct _FTCOMSTAT { + DWORD fCtsHold : 1; + DWORD fDsrHold : 1; + DWORD fRlsdHold : 1; + DWORD fXoffHold : 1; + DWORD fXoffSent : 1; + DWORD fEof : 1; + DWORD fTxim : 1; + DWORD fReserved : 25; + DWORD cbInQue; + DWORD cbOutQue; + } FTCOMSTAT, *LPFTCOMSTAT; + + typedef struct _FTDCB { + DWORD DCBlength; /* sizeof(FTDCB) */ + DWORD BaudRate; /* Baudrate at which running */ + DWORD fBinary: 1; /* Binary Mode (skip EOF check) */ + DWORD fParity: 1; /* Enable parity checking */ + DWORD fOutxCtsFlow:1; /* CTS handshaking on output */ + DWORD fOutxDsrFlow:1; /* DSR handshaking on output */ + DWORD fDtrControl:2; /* DTR Flow control */ + DWORD fDsrSensitivity:1; /* DSR Sensitivity */ + DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */ + DWORD fOutX: 1; /* Enable output X-ON/X-OFF */ + DWORD fInX: 1; /* Enable input X-ON/X-OFF */ + DWORD fErrorChar: 1; /* Enable Err Replacement */ + DWORD fNull: 1; /* Enable Null stripping */ + DWORD fRtsControl:2; /* Rts Flow control */ + DWORD fAbortOnError:1; /* Abort all reads and writes on Error */ + DWORD fDummy2:17; /* Reserved */ + WORD wReserved; /* Not currently used */ + WORD XonLim; /* Transmit X-ON threshold */ + WORD XoffLim; /* Transmit X-OFF threshold */ + BYTE ByteSize; /* Number of bits/byte, 4-8 */ + BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ + BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ + char XonChar; /* Tx and Rx X-ON character */ + char XoffChar; /* Tx and Rx X-OFF character */ + char ErrorChar; /* Error replacement char */ + char EofChar; /* End of Input character */ + char EvtChar; /* Received Event character */ + WORD wReserved1; /* Fill for now. */ + } FTDCB, *LPFTDCB; + + typedef struct _FTTIMEOUTS { + DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ + DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ + DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ + } FTTIMEOUTS,*LPFTTIMEOUTS; + + + FTD2XX_API + BOOL WINAPI FT_W32_ClearCommBreak( + FT_HANDLE ftHandle + ); + + FTD2XX_API + BOOL WINAPI FT_W32_ClearCommError( + FT_HANDLE ftHandle, + LPDWORD lpdwErrors, + LPFTCOMSTAT lpftComstat + ); + + FTD2XX_API + BOOL WINAPI FT_W32_EscapeCommFunction( + FT_HANDLE ftHandle, + DWORD dwFunc + ); + + FTD2XX_API + BOOL WINAPI FT_W32_GetCommModemStatus( + FT_HANDLE ftHandle, + LPDWORD lpdwModemStatus + ); + + FTD2XX_API + BOOL WINAPI FT_W32_GetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + + FTD2XX_API + BOOL WINAPI FT_W32_GetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + + FTD2XX_API + BOOL WINAPI FT_W32_PurgeComm( + FT_HANDLE ftHandle, + DWORD dwMask + ); + + FTD2XX_API + BOOL WINAPI FT_W32_SetCommBreak( + FT_HANDLE ftHandle + ); + + FTD2XX_API + BOOL WINAPI FT_W32_SetCommMask( + FT_HANDLE ftHandle, + ULONG ulEventMask + ); + + FTD2XX_API + BOOL WINAPI FT_W32_GetCommMask( + FT_HANDLE ftHandle, + LPDWORD lpdwEventMask + ); + + FTD2XX_API + BOOL WINAPI FT_W32_SetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + + FTD2XX_API + BOOL WINAPI FT_W32_SetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + + FTD2XX_API + BOOL WINAPI FT_W32_SetupComm( + FT_HANDLE ftHandle, + DWORD dwReadBufferSize, + DWORD dwWriteBufferSize + ); + + FTD2XX_API + BOOL WINAPI FT_W32_WaitCommEvent( + FT_HANDLE ftHandle, + PULONG pulEvent, + LPOVERLAPPED lpOverlapped + ); + + + // + // Device information + // + + typedef struct _ft_device_list_info_node { + ULONG Flags; + ULONG Type; + ULONG ID; + DWORD LocId; + char SerialNumber[16]; + char Description[64]; + FT_HANDLE ftHandle; + } FT_DEVICE_LIST_INFO_NODE; + + // Device information flags + enum { + FT_FLAGS_OPENED = 1, + FT_FLAGS_HISPEED = 2 + }; + + + FTD2XX_API + FT_STATUS WINAPI FT_CreateDeviceInfoList( + LPDWORD lpdwNumDevs + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetDeviceInfoList( + FT_DEVICE_LIST_INFO_NODE *pDest, + LPDWORD lpdwNumDevs + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetDeviceInfoDetail( + DWORD dwIndex, + LPDWORD lpdwFlags, + LPDWORD lpdwType, + LPDWORD lpdwID, + LPDWORD lpdwLocId, + LPVOID lpSerialNumber, + LPVOID lpDescription, + FT_HANDLE *pftHandle + ); + + + // + // Version information + // + + FTD2XX_API + FT_STATUS WINAPI FT_GetDriverVersion( + FT_HANDLE ftHandle, + LPDWORD lpdwVersion + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetLibraryVersion( + LPDWORD lpdwVersion + ); + + + FTD2XX_API + FT_STATUS WINAPI FT_Rescan( + void + ); + + FTD2XX_API + FT_STATUS WINAPI FT_Reload( + WORD wVid, + WORD wPid + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetComPortNumber( + FT_HANDLE ftHandle, + LPLONG lpdwComPortNumber + ); + + + // + // FT232H additional EEPROM functions + // + + FTD2XX_API + FT_STATUS WINAPI FT_EE_ReadConfig( + FT_HANDLE ftHandle, + UCHAR ucAddress, + PUCHAR pucValue + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_WriteConfig( + FT_HANDLE ftHandle, + UCHAR ucAddress, + UCHAR ucValue + ); + + FTD2XX_API + FT_STATUS WINAPI FT_EE_ReadECC( + FT_HANDLE ftHandle, + UCHAR ucOption, + LPWORD lpwValue + ); + + FTD2XX_API + FT_STATUS WINAPI FT_GetQueueStatusEx( + FT_HANDLE ftHandle, + DWORD *dwRxBytes + ); + + +#ifdef __cplusplus +} +#endif + + +#endif /* FTD2XX_H */ + diff --git a/analyzer.cpp b/analyzer.cpp new file mode 100644 index 0000000..c4ccfb3 --- /dev/null +++ b/analyzer.cpp @@ -0,0 +1,60 @@ +// analyzer.cpp + +#include "analyzer.h" + +// --- event lister --------------------------------------------------------- + +class CStore : public CAnalyzer +{ + CRocEvent* Read(); +}; + + +CRocEvent* CStore::Read() +{ + CRocEvent *data = Get(); + printf("%8u: %03X %4u:\n", data->eventNr, (unsigned int)(data->header), (unsigned int)(data->pixel.size())); + return data; +} + + +// --- column statistics ---------------------------------------------------- + +class CColActivity : public CAnalyzer +{ + unsigned long colhits[52]; + CRocEvent* Read(); +public: + CColActivity() { Clear(); } + void Clear(); +}; + + +void CColActivity::Clear() +{ + for (int i=0; i<52; i++) colhits[i] = 0; +} + + +CRocEvent* CColActivity::Read() +{ + CRocEvent *data = Get(); + list::iterator i; + for (i = data->pixel.begin(); i != data->pixel.end(); i++) + if (i->x >= 0 && i->x < 52) colhits[i->x]++; + return data; +} + + +void Analyzer(CTestboard &tb) +{ + CBinaryDTBSource src(tb); + CDataRecordScanner rec; + CRocDecoder dec; + CStore lister; + CSink pump; + + src >> rec >> dec >> lister >> pump; + + pump.GetAll(); +} diff --git a/analyzer.h b/analyzer.h new file mode 100644 index 0000000..25bdf3b --- /dev/null +++ b/analyzer.h @@ -0,0 +1,7 @@ +// analyzer.h + +#pragma once + +#include "pixel_dtb.h" +#include "datastream.h" + diff --git a/color.cpp b/color.cpp new file mode 100644 index 0000000..a9235dc --- /dev/null +++ b/color.cpp @@ -0,0 +1,134 @@ +// color.cpp + + +#include "color.h" + + +const CColor COLOR_TABLE[SIZE_OF_COLORTABLE] = +{ + { 0.0, 0.0, 0.0 }, // 0 black + { 0.5, 0.5, 0.5 }, // 1 dark gray + { .75, .75, .75 }, // 2 light gray + { 1.0, 1.0, 1.0 }, // 3 white + + { .75, 0.0, 0.0 }, // 4 dark red + { 1.0, 0.0, 0.0 }, // 5 red + { 1.0, 0.4, 0.5 }, // 6 light red + + { 0.0, 0.5, 0.0 }, // 7 dark green + { 0.0, .75, 0.0 }, // 8 green + { 0.0, 1.0, 0.0 }, // 9 green + { .75, 1.0, 0.0 }, // 10 yellow green + { 0.7, 1.0, 0.7 }, // 11 light green + + { 0.0, 0.5, 0.5 }, // 12 dark blue green + + { 0.0, 0.0, 1.0 }, // 13 dark blue + { .25, 0.5, 1.0 }, // 14 blue + { 0.0, .75, 1.0 }, // 15 blue + { 0.5, .75, 1.0 }, // 16 blue + { 0.0, 1.0, 1.0 }, // 17 light blue + { 0.5, 1.0, 1.0 }, // 18 light blue + + { 0.5, .25, 0.0 }, // 19 dark brown + { .75, 0.5, 0.0 }, // 20 brown + { 1.0, 0.5, .25 }, // 21 orange + { 1.0, .75, 0.5 }, // 22 light orange + { 1.0, 1.0, 0.0 }, // 23 yellow + { 1.0, 1.0, .75 }, // 24 light yellow + + { 0.5, 0.0, 0.5 }, // 25 dark red blue + { 1.0, 0.0, 1.0 }, // 26 red blue + { 1.0, .75, 1.0 } // 27 light red blue +}; + + + +const int COLOR_BIN[] = +{ + 8, + 5, + 23, + 21, + 24, + 25, + 26, + 27, + 12, + 13, + 14, + 16, + 18 +}; + +const int COLOR_FAIL[] = +{ + 5, + 23, + 21, + 24, + 25, + 26, + 13, + 14, + 17, + 0, + 1, + 2, + 3, + 7, + 19, + 20, + 22, + + 12, + 27, + 4, + + 6, + 11, + 10, + 8 +}; + +const int COLOR_CLASS[] = +{ + 8, + 10, + 17, + 23, + 5 +}; + +const int COLOR_PIC_GOOD[] = +{ + 7, + 18, + 8, + 17, + 9, + 16, + 10, + 15, + 11, + 14, + 12, + 13 +}; + +const int COLOR_PIC_CL2 = 20; + +const int COLOR_PIC_BAD[] = +{ + 5, + 23, + 21, + 4, + 24, + 6, + 22, + 1, + 2, + 3 +}; + diff --git a/color.h b/color.h new file mode 100644 index 0000000..6b82faf --- /dev/null +++ b/color.h @@ -0,0 +1,27 @@ +// color.h + +#ifndef COLOR_H +#define COLOR_H + + +struct CColor { float r, g, b; }; + + +#define SIZE_OF_COLORTABLE 28 + +extern const CColor COLOR_TABLE[SIZE_OF_COLORTABLE]; + +extern const int COLOR_BIN[]; + +extern const int COLOR_FAIL[]; + +extern const int COLOR_CLASS[]; + +extern const int COLOR_PIC_GOOD[]; + +extern const int COLOR_PIC_CL2; + +extern const int COLOR_PIC_BAD[]; + + +#endif diff --git a/datapipe.h b/datapipe.h new file mode 100644 index 0000000..d9bef27 --- /dev/null +++ b/datapipe.h @@ -0,0 +1,76 @@ +// connect.h + +#ifndef CONNECT_H +#define CONNECT_H + + +// === data source ========================================================== + +// The inheritor must define ReadLast and Read +template +class CSource +{ + virtual T ReadLast() = 0; + virtual T Read() = 0; + + template friend class CSink; +}; + +// Null source for not connected sinks +template +class CNullSource : public CSource +{ +protected: + CNullSource() {} + CNullSource(const CNullSource&) {} + ~CNullSource() {} + T ReadLast() { throw "no connection"; } + T Read() { return ReadLast(); } + template friend class CSink; +}; + + +// === data sink ============================================================ + +template +class CSink +{ +public: + CSource *src; + static CNullSource null; +public: + CSink() : src(&null) {} + + T GetLast() { return src->ReadLast(); } + T Get() { return src->Read(); } + void GetAll() { while (true) Get(); } +}; + +template CNullSource CSink::null; + + +// === data in -> out (pipe) ================================================ + +template +class CDataPipe : public CSink, public CSource {}; + + +// === operators to connect pipes =========================================== + +// source -> sink; source -> datapipe +template +void operator >> (CSource &in, CSink &out) +{ + out.src = ∈ +} + +// source -> datapipe -> datapipe -> sink +template +CSource& operator >> (CSource &in, CDataPipe &out) +{ + out.src = ∈ + return out; +} + + +#endif diff --git a/datastream.cpp b/datastream.cpp new file mode 100644 index 0000000..735bceb --- /dev/null +++ b/datastream.cpp @@ -0,0 +1,113 @@ +// datastream.cpp + +#include "datastream.h" +#include "protocol.h" + +#ifdef _WIN32 +#define QWFMT "%15I64u" // !!! only for windows +#else +#define QWFMT "%15llu" // !!! probably works for Linux +#endif + +// === Data structures ====================================================== + +void CDataRecord::Print(CProtocol &f) +{ + +} + + +void CPixel::DecodeRaw() +{ + unsigned int temp = raw; + pulseheight = (temp & 0x0f) + ((temp >> 1) & 0xf0); + temp >>= 9; + int c = (temp >> 12) & 7; + c = c*6 + ((temp >> 9) & 7); + int r = (temp >> 6) & 7; + r = r*6 + ((temp >> 3) & 7); + r = r*6 + ( temp & 7); + y = 80 - r/2; + x = 2*c + (r&1); +} + + +// === CBinaryDTBSource (CSource) ================================ + +uint16_t CBinaryDTBSource::FillBuffer() +{ + uint32_t data_available; + pos = 0; + do + { + tb->Daq_Read(buffer, data_available, DTB_SOURCE_BLOCK_SIZE); + if (data_available < 100000) + { + if (data_available > 1000) tb->mDelay( 5); + else if (data_available > 0) tb->mDelay( 50); + else tb->mDelay(500); + + } + } while(buffer.size() == 0); + + return lastSample = buffer[pos++]; +} + + +// === CBinaryFileSource (CSource) ================================ + +uint16_t CBinaryFileSource::FillBuffer() +{ + pos = 0; + do + { + buffer.resize(FILE_SOURCE_BLOCK_SIZE); + size = fread(buffer.data(), sizeof(uint16_t), FILE_SOURCE_BLOCK_SIZE, f); + } while(size == 0); + + return lastSample = buffer[pos++]; +} + + +// === CDataRecordScanner (CDataPipe) ============= + +CDataRecord* CDataRecordScanner::Read() +{ + record.eventNr = currentEventNr++; + record.data.clear(); + while (!(GetLast() & 0x8000)) Get(); + do + { + if (record.data.size() >= 40000) break; + record.data.push_back(GetLast()); + + } while (!(Get() & 0x8000)); + + return &record; +} + + +// === CDecoder (CDataRecord*, CEvent*) ===================================== + +CRocEvent* CRocDecoder::Read() +{ + CDataRecord *sample = Get(); + roc_event.eventNr = sample->eventNr; + roc_event.header = 0; + roc_event.pixel.clear(); + unsigned int n = sample->data.size(); + if (n > 0) + { + roc_event.header = sample->data[0] & 0xfff; + unsigned int pos = 2; + while (pos < n-1) + { + CPixel pix; + pix.raw = (sample->data[pos++] & 0xfff) << 12; + pix.raw += sample->data[pos++] & 0xfff; + pix.DecodeRaw(); + roc_event.pixel.push_back(pix); + } + } + return &roc_event; +} diff --git a/datastream.h b/datastream.h new file mode 100644 index 0000000..82f58d4 --- /dev/null +++ b/datastream.h @@ -0,0 +1,119 @@ +// datastream.h + +#pragma once + +#include +#include +#include + +#include "psi46test.h" +#include "datapipe.h" +#include "protocol.h" + +using namespace std; + + +// === Binary Data Record Format ============================================ + +struct CDataRecord +{ + unsigned int eventNr; + vector data; + void Print(CProtocol &f); +}; + + +struct CPixel +{ + int raw; + int x; + int y; + int pulseheight; + void DecodeRaw(); +}; + + +struct CRocEvent +{ + unsigned long eventNr; + unsigned short header; + list pixel; +}; + + +// === Data Sources ========================================================= + +// --- pixelDTB + +#define DTB_SOURCE_BLOCK_SIZE 16384 + +class CBinaryDTBSource : public CSource +{ + CTestboard *tb; + uint16_t lastSample; + + unsigned int pos; + vector buffer; + uint16_t FillBuffer(); + + uint16_t Read() { return (pos < buffer.size()) ? lastSample = buffer[pos++] : FillBuffer(); } + uint16_t ReadLast() { return lastSample; } +public: + CBinaryDTBSource(CTestboard &src) : tb(&src), pos(0), lastSample(0) { buffer.reserve(DTB_SOURCE_BLOCK_SIZE); } +}; + + +// --- File + +#define FILE_SOURCE_BLOCK_SIZE 16384 + +class CBinaryFileSource : public CSource +{ + FILE *f; + uint16_t lastSample; + + unsigned int size; + unsigned int pos; + vector buffer; + uint16_t FillBuffer(); + + uint16_t Read() { return (pos < size) ? lastSample = buffer[pos++] : FillBuffer(); } + uint16_t ReadLast() { return lastSample; } +public: + CBinaryFileSource() : f(0), size(0), pos(0), lastSample(0) { buffer.reserve(FILE_SOURCE_BLOCK_SIZE); } + ~CBinaryFileSource() { Close(); } + bool Open(const char *filename) { return (f = fopen(filename, "rb")) != 0; } + void Close() { if (f) { fclose(f); f = 0; } } +}; + + +// === CDataRecordScanner (CDataPipe) =================== + +class CDataRecordScanner : public CDataPipe +{ + unsigned long currentEventNr; + CDataRecord record; + CDataRecord* Read(); + CDataRecord* ReadLast() { return &record; } +public: + CDataRecordScanner() : currentEventNr(0) {} +}; + + +// === CDecoder (CDataRecord*, CRocEvent*) ================================== + +class CRocDecoder : public CDataPipe +{ + CRocEvent roc_event; + CRocEvent* Read(); + CRocEvent* ReadLast() { return &roc_event; } +}; + + +// === CAnalyzer (CRocEvent*, CRocEvent*) =================================== + +class CAnalyzer : public CDataPipe +{ + CRocEvent* Read() { return Get(); }; + CRocEvent* ReadLast() { return GetLast(); } +};