From 2cebf335767a97fccdc6cf5673a23e6fb4a829aa Mon Sep 17 00:00:00 2001 From: Dmitriy Anisimov Date: Fri, 19 Dec 2014 14:41:36 +0300 Subject: [PATCH 1/2] removed CImg --- deps/CImg/CImg.h | 46469 --------- deps/CImg/Licence_CeCILL-C_V1-en.txt | 508 - deps/CImg/Licence_CeCILL_V2-en.txt | 504 - deps/CImg/README.txt | 173 - deps/CImg/examples/CImg_demo | Bin 2067704 -> 0 bytes deps/CImg/examples/CImg_demo.cpp | 1609 - deps/CImg/examples/Makefile | 596 - deps/CImg/examples/captcha | Bin 523200 -> 0 bytes deps/CImg/examples/captcha.cpp | 154 - deps/CImg/examples/curve_editor2d | Bin 785456 -> 0 bytes deps/CImg/examples/curve_editor2d.cpp | 355 - deps/CImg/examples/dtmri_view3d | Bin 1043568 -> 0 bytes deps/CImg/examples/dtmri_view3d.cpp | 552 - deps/CImg/examples/edge_explorer2d | Bin 596960 -> 0 bytes deps/CImg/examples/edge_explorer2d.cpp | 217 - deps/CImg/examples/fade_images | Bin 883808 -> 0 bytes deps/CImg/examples/fade_images.cpp | 94 - deps/CImg/examples/gaussian_fit1d | Bin 478144 -> 0 bytes deps/CImg/examples/gaussian_fit1d.cpp | 168 - deps/CImg/examples/generate_loop_macros | Bin 26792 -> 0 bytes deps/CImg/examples/generate_loop_macros.cpp | 338 - deps/CImg/examples/hough_transform2d | Bin 621568 -> 0 bytes deps/CImg/examples/hough_transform2d.cpp | 145 - deps/CImg/examples/image2ascii | Bin 379456 -> 0 bytes deps/CImg/examples/image2ascii.cpp | 156 - deps/CImg/examples/image_registration2d | Bin 773160 -> 0 bytes deps/CImg/examples/image_registration2d.cpp | 214 - deps/CImg/examples/image_surface3d | Bin 937056 -> 0 bytes deps/CImg/examples/image_surface3d.cpp | 140 - deps/CImg/examples/img/CImg_demo.h | 31909 ------- deps/CImg/examples/img/lena.pgm | 6 - deps/CImg/examples/img/logo.bmp | Bin 90534 -> 0 bytes deps/CImg/examples/img/milla.bmp | Bin 153966 -> 0 bytes deps/CImg/examples/img/odykill.h | 79162 ---------------- deps/CImg/examples/img/parrot_mask.pgm | Bin 246569 -> 0 bytes deps/CImg/examples/img/parrot_original.ppm | Bin 739676 -> 0 bytes deps/CImg/examples/img/sh0r.pgm | 256 - deps/CImg/examples/img/sh1r.pgm | 243 - deps/CImg/examples/img/tetris.h | 2313 - deps/CImg/examples/jawbreaker | Bin 514960 -> 0 bytes deps/CImg/examples/jawbreaker.cpp | 231 - deps/CImg/examples/mcf_levelsets2d | Bin 461664 -> 0 bytes deps/CImg/examples/mcf_levelsets2d.cpp | 119 - deps/CImg/examples/mcf_levelsets3d | Bin 1125472 -> 0 bytes deps/CImg/examples/mcf_levelsets3d.cpp | 179 - deps/CImg/examples/odykill | Bin 1743816 -> 0 bytes deps/CImg/examples/odykill.cpp | 222 - deps/CImg/examples/pde_TschumperleDeriche2d | Bin 891952 -> 0 bytes .../examples/pde_TschumperleDeriche2d.cpp | 227 - deps/CImg/examples/pde_heatflow2d | Bin 609232 -> 0 bytes deps/CImg/examples/pde_heatflow2d.cpp | 114 - deps/CImg/examples/plotter1d | Bin 719952 -> 0 bytes deps/CImg/examples/plotter1d.cpp | 73 - deps/CImg/examples/radon_transform2d | Bin 609296 -> 0 bytes deps/CImg/examples/radon_transform2d.cpp | 378 - deps/CImg/examples/scene3d | Bin 920640 -> 0 bytes deps/CImg/examples/scene3d.cpp | 141 - deps/CImg/examples/spherical_function3d | Bin 809984 -> 0 bytes deps/CImg/examples/spherical_function3d.cpp | 112 - deps/CImg/examples/tetris | Bin 506816 -> 0 bytes deps/CImg/examples/tetris.cpp | 196 - deps/CImg/examples/tron | Bin 445328 -> 0 bytes deps/CImg/examples/tron.cpp | 186 - deps/CImg/examples/tutorial | Bin 596992 -> 0 bytes deps/CImg/examples/tutorial.cpp | 131 - deps/CImg/examples/use_RGBclass | Bin 1104976 -> 0 bytes deps/CImg/examples/use_RGBclass.cpp | 138 - deps/CImg/examples/use_chlpca | Bin 986224 -> 0 bytes deps/CImg/examples/use_chlpca.cpp | 70 - deps/CImg/examples/use_cimgIPL.cpp | 155 - deps/CImg/examples/use_cimgmatlab.cpp | 102 - deps/CImg/examples/use_cimgmatlab.m | 33 - deps/CImg/examples/use_draw_gradient | Bin 871552 -> 0 bytes deps/CImg/examples/use_draw_gradient.cpp | 138 - deps/CImg/examples/use_jpeg_buffer.cpp | 109 - deps/CImg/examples/use_nlmeans | Bin 1023104 -> 0 bytes deps/CImg/examples/use_nlmeans.cpp | 125 - deps/CImg/examples/use_skeleton | Bin 1121376 -> 0 bytes deps/CImg/examples/use_skeleton.cpp | 119 - deps/CImg/examples/use_tiff_stream.cpp | 81 - deps/CImg/examples/wavelet_atrous | Bin 707576 -> 0 bytes deps/CImg/examples/wavelet_atrous.cpp | 194 - deps/CImg/plugins/add_fileformat.h | 79 - deps/CImg/plugins/chlpca.h | 317 - deps/CImg/plugins/cimgIPL.h | 122 - deps/CImg/plugins/cimg_ipl.h | 303 - deps/CImg/plugins/cimgmatlab.h | 287 - deps/CImg/plugins/draw_gradient.h | 250 - deps/CImg/plugins/jpeg_buffer.h | 374 - deps/CImg/plugins/loop_macros.h | 24166 ----- deps/CImg/plugins/nlmeans.h | 240 - deps/CImg/plugins/opencv.h | 112 - deps/CImg/plugins/skeleton.h | 580 - deps/CImg/plugins/tiff_stream.h | 188 - deps/CImg/plugins/vrml.h | 788 - deps/CImg/plugins/vtk.h | 103 - deps/CImg/resources/CImg.pc | 8 - deps/CImg/resources/cimg_buildpackage | 167 - deps/CImg/resources/compile_win_icl.bat | 12 - deps/CImg/resources/compile_win_visualcpp.bat | 12 - deps/CImg/resources/debian/changelog | 5 - deps/CImg/resources/debian/cimg-dev.dirs | 3 - deps/CImg/resources/debian/cimg-dev.install | 3 - deps/CImg/resources/debian/cimg-dev.links | 3 - deps/CImg/resources/debian/compat | 1 - deps/CImg/resources/debian/control | 22 - deps/CImg/resources/debian/copyright | 1035 - deps/CImg/resources/debian/docs | 1 - deps/CImg/resources/debian/rules | 80 - .../project_win_visualcpp.sln | 20 - .../project_win_visualcpp.suo | Bin 7168 -> 0 bytes .../project_win_visualcpp.vcproj | 200 - 112 files changed, 199335 deletions(-) delete mode 100644 deps/CImg/CImg.h delete mode 100644 deps/CImg/Licence_CeCILL-C_V1-en.txt delete mode 100644 deps/CImg/Licence_CeCILL_V2-en.txt delete mode 100644 deps/CImg/README.txt delete mode 100755 deps/CImg/examples/CImg_demo delete mode 100644 deps/CImg/examples/CImg_demo.cpp delete mode 100644 deps/CImg/examples/Makefile delete mode 100755 deps/CImg/examples/captcha delete mode 100644 deps/CImg/examples/captcha.cpp delete mode 100755 deps/CImg/examples/curve_editor2d delete mode 100644 deps/CImg/examples/curve_editor2d.cpp delete mode 100755 deps/CImg/examples/dtmri_view3d delete mode 100644 deps/CImg/examples/dtmri_view3d.cpp delete mode 100755 deps/CImg/examples/edge_explorer2d delete mode 100644 deps/CImg/examples/edge_explorer2d.cpp delete mode 100755 deps/CImg/examples/fade_images delete mode 100644 deps/CImg/examples/fade_images.cpp delete mode 100755 deps/CImg/examples/gaussian_fit1d delete mode 100644 deps/CImg/examples/gaussian_fit1d.cpp delete mode 100755 deps/CImg/examples/generate_loop_macros delete mode 100644 deps/CImg/examples/generate_loop_macros.cpp delete mode 100755 deps/CImg/examples/hough_transform2d delete mode 100644 deps/CImg/examples/hough_transform2d.cpp delete mode 100755 deps/CImg/examples/image2ascii delete mode 100644 deps/CImg/examples/image2ascii.cpp delete mode 100755 deps/CImg/examples/image_registration2d delete mode 100644 deps/CImg/examples/image_registration2d.cpp delete mode 100755 deps/CImg/examples/image_surface3d delete mode 100644 deps/CImg/examples/image_surface3d.cpp delete mode 100644 deps/CImg/examples/img/CImg_demo.h delete mode 100644 deps/CImg/examples/img/lena.pgm delete mode 100644 deps/CImg/examples/img/logo.bmp delete mode 100644 deps/CImg/examples/img/milla.bmp delete mode 100644 deps/CImg/examples/img/odykill.h delete mode 100644 deps/CImg/examples/img/parrot_mask.pgm delete mode 100644 deps/CImg/examples/img/parrot_original.ppm delete mode 100644 deps/CImg/examples/img/sh0r.pgm delete mode 100644 deps/CImg/examples/img/sh1r.pgm delete mode 100644 deps/CImg/examples/img/tetris.h delete mode 100755 deps/CImg/examples/jawbreaker delete mode 100644 deps/CImg/examples/jawbreaker.cpp delete mode 100755 deps/CImg/examples/mcf_levelsets2d delete mode 100644 deps/CImg/examples/mcf_levelsets2d.cpp delete mode 100755 deps/CImg/examples/mcf_levelsets3d delete mode 100644 deps/CImg/examples/mcf_levelsets3d.cpp delete mode 100755 deps/CImg/examples/odykill delete mode 100644 deps/CImg/examples/odykill.cpp delete mode 100755 deps/CImg/examples/pde_TschumperleDeriche2d delete mode 100644 deps/CImg/examples/pde_TschumperleDeriche2d.cpp delete mode 100755 deps/CImg/examples/pde_heatflow2d delete mode 100644 deps/CImg/examples/pde_heatflow2d.cpp delete mode 100755 deps/CImg/examples/plotter1d delete mode 100644 deps/CImg/examples/plotter1d.cpp delete mode 100755 deps/CImg/examples/radon_transform2d delete mode 100644 deps/CImg/examples/radon_transform2d.cpp delete mode 100755 deps/CImg/examples/scene3d delete mode 100644 deps/CImg/examples/scene3d.cpp delete mode 100755 deps/CImg/examples/spherical_function3d delete mode 100644 deps/CImg/examples/spherical_function3d.cpp delete mode 100755 deps/CImg/examples/tetris delete mode 100644 deps/CImg/examples/tetris.cpp delete mode 100755 deps/CImg/examples/tron delete mode 100644 deps/CImg/examples/tron.cpp delete mode 100755 deps/CImg/examples/tutorial delete mode 100644 deps/CImg/examples/tutorial.cpp delete mode 100755 deps/CImg/examples/use_RGBclass delete mode 100644 deps/CImg/examples/use_RGBclass.cpp delete mode 100755 deps/CImg/examples/use_chlpca delete mode 100644 deps/CImg/examples/use_chlpca.cpp delete mode 100644 deps/CImg/examples/use_cimgIPL.cpp delete mode 100644 deps/CImg/examples/use_cimgmatlab.cpp delete mode 100644 deps/CImg/examples/use_cimgmatlab.m delete mode 100755 deps/CImg/examples/use_draw_gradient delete mode 100644 deps/CImg/examples/use_draw_gradient.cpp delete mode 100644 deps/CImg/examples/use_jpeg_buffer.cpp delete mode 100755 deps/CImg/examples/use_nlmeans delete mode 100644 deps/CImg/examples/use_nlmeans.cpp delete mode 100755 deps/CImg/examples/use_skeleton delete mode 100644 deps/CImg/examples/use_skeleton.cpp delete mode 100644 deps/CImg/examples/use_tiff_stream.cpp delete mode 100755 deps/CImg/examples/wavelet_atrous delete mode 100644 deps/CImg/examples/wavelet_atrous.cpp delete mode 100644 deps/CImg/plugins/add_fileformat.h delete mode 100644 deps/CImg/plugins/chlpca.h delete mode 100644 deps/CImg/plugins/cimgIPL.h delete mode 100644 deps/CImg/plugins/cimg_ipl.h delete mode 100644 deps/CImg/plugins/cimgmatlab.h delete mode 100644 deps/CImg/plugins/draw_gradient.h delete mode 100644 deps/CImg/plugins/jpeg_buffer.h delete mode 100644 deps/CImg/plugins/loop_macros.h delete mode 100644 deps/CImg/plugins/nlmeans.h delete mode 100644 deps/CImg/plugins/opencv.h delete mode 100644 deps/CImg/plugins/skeleton.h delete mode 100644 deps/CImg/plugins/tiff_stream.h delete mode 100644 deps/CImg/plugins/vrml.h delete mode 100644 deps/CImg/plugins/vtk.h delete mode 100644 deps/CImg/resources/CImg.pc delete mode 100755 deps/CImg/resources/cimg_buildpackage delete mode 100644 deps/CImg/resources/compile_win_icl.bat delete mode 100644 deps/CImg/resources/compile_win_visualcpp.bat delete mode 100644 deps/CImg/resources/debian/changelog delete mode 100644 deps/CImg/resources/debian/cimg-dev.dirs delete mode 100644 deps/CImg/resources/debian/cimg-dev.install delete mode 100644 deps/CImg/resources/debian/cimg-dev.links delete mode 100644 deps/CImg/resources/debian/compat delete mode 100644 deps/CImg/resources/debian/control delete mode 100644 deps/CImg/resources/debian/copyright delete mode 100644 deps/CImg/resources/debian/docs delete mode 100755 deps/CImg/resources/debian/rules delete mode 100644 deps/CImg/resources/project_win_visualcpp/project_win_visualcpp.sln delete mode 100644 deps/CImg/resources/project_win_visualcpp/project_win_visualcpp.suo delete mode 100644 deps/CImg/resources/project_win_visualcpp/project_win_visualcpp.vcproj diff --git a/deps/CImg/CImg.h b/deps/CImg/CImg.h deleted file mode 100644 index 103686f..0000000 --- a/deps/CImg/CImg.h +++ /dev/null @@ -1,46469 +0,0 @@ -/* - # - # 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 159 - -/*----------------------------------------------------------- - # - # 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" -#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" { -#define uint64 uint64_hack_ -#define int64 int64_hack_ -#include "tiffio.h" -#undef uint64 -#undef int64 -} -#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 -#include -#include -} -#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&, ...) {} - - // [internal] Lock/unlock a mutex for managing concurrent threads. - // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }. - // 'n' can be in [0,31] but mutex range [0,16] is reserved by CImg. - inline int mutex(const unsigned int n, const int lock_mode=1); - - inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) { - static unsigned int mode = cimg_verbosity; - cimg::mutex(0); - if (is_set) mode = value; - cimg::mutex(0,0); - 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::min() || val>cimg::type::max()); -#endif - } - static bool is_nan(const double val) { -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#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) { -#ifdef isinf - return (bool)isinf(val); -#else - return !is_nan(val) && (val::min() || val>cimg::type::max()); -#endif - } - static bool is_nan(const float val) { -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif - } - 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 double type; }; - template<> struct superset { typedef double type; }; - template<> struct superset { typedef double 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* events_thread; - pthread_cond_t wait_event; - pthread_mutex_t wait_event_mutex; - 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),events_thread(0),display(0), - nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { - XInitThreads(); - pthread_mutex_init(&wait_event_mutex,0); - pthread_cond_init(&wait_event,0); -#ifdef cimg_use_xrandr - resolutions = 0; - curr_rotation = 0; - curr_resolution = nb_resolutions = 0; -#endif - } - - ~X11_info() { - if (events_thread) { - pthread_cancel(*events_thread); - delete events_thread; - } - if (display) {} // XCloseDisplay(display); - pthread_cond_destroy(&wait_event); - pthread_mutex_unlock(&wait_event_mutex); - pthread_mutex_destroy(&wait_event_mutex); - } - }; -#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 - - struct Mutex_info { -#if cimg_OS==2 - HANDLE mutex[32]; - Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); } - void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); } - void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); } - int trylock(const unsigned int) { return 0; } -#elif defined(_PTHREAD_H) - pthread_mutex_t mutex[32]; - Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); } - void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); } - void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); } - int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); } -#else - Mutex_info() {} - void lock(const unsigned int) {} - void unlock(const unsigned int) {} - int trylock(const unsigned int) { return 0; } -#endif - }; -#if defined(cimg_module) - Mutex_info& Mutex_attr(); -#elif defined(cimg_main) - Mutex_info& Mutex_attr() { static Mutex_info val; return val; } -#else - inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; } -#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 12x13 font (small size). - const char *const data_font12x13 = -" .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxl" -"wlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b{ \\w Wx`xTw_w[wbxawSwkw nynwkyw bwswcwkwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw" -"rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wtwmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlw" -"twpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawswowswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw" -"rwpwrwpwswoznwtw Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuwnwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwew" -"ewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqwuwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux" -"myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqxuwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nw" -"uwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnwtwmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw" -"uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpwrwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwow" -"rwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnynwtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws" -"wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwtwqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmws" -"wowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwtwqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo" -"yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswo" -"wswowswowkwkwkwkwswowswowswowswowswowswowswcwtxowswowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux" -"qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmwkwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkw" -"jwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswswowswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw" -"twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtwhwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswow" -"swowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow" -"swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmws" -"wkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwkwtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws" -"wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowtwtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwu" -"xqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owkxuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq" -"wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkwswowswowswowswowswowswowswcwuwuwowswowswowswowswowt" -"wnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxmw bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw" -"swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswswswswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTx" -"uxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtwnwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw" -"tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtxpxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxow" -"kwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz" -"mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzozmymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznx" -"lwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pwtxn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw" -"qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynwswnymymymymybzmznznznznwlzmw hwHwlwSwTw {+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr" -"bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms_snqbsnqbsnqbsnqaq`qns_q !p Zp jp#q\\q6q7q l" -"q [sjq Qq -q OqZq]q Cq;q HqWq $rIq`qZq _q iqbqKqFqIq`q hp$q]u JqYpmpLp .p jp ]p Xr`q[r !p Tp\"p\\p6q6q mq Yx Qr -r Ps\\q_s" -" Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q hp$q]t IqYpmpLq /q kq Fq_q[q #s Tp\"q^q6p 1p Vu Rs YsJsMy &v])]2_4^U^ 6^T\\5])]1_2]T\\8^U^ K])]2`4^V^3] " -" U]*\\2a4`V\\8^U^5a F]*\\1\\X\\4^U^=]*\\" -"2a5^U^ 7aV\\4]*\\1a4`V\\8^U^ J]*\\1\\X\\4^V^3\\ " -" S],\\1\\W\\5g8^U^6c F],\\1\\V\\5^U^<],\\2]W]6^U^ 8h3],\\0\\W\\5g8^U^ I],\\1\\V\\5^V" -"^4\\ ;] " -" :\\-]2\\U\\6\\V`7^U^7]U] F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\Va1\\-]1\\U\\6\\V`7^U^ H\\-]2\\T\\6^V^5] =a " -" J] " -" N\\/]2\\S\\7\\T]6^U^7\\S\\ E\\/]2\\R\\7^U^:\\/]3]S]8^U^ 8\\T^/\\/]1\\S\\7\\T]6^U^ G\\/]2\\R\\7^V^6] =c L^ " -" *^ U` O^ )\\S\\ " -" !^$^3\\ E]U\\ K^$^4^ G^$^4] J^$^3\\ #^$^3\\ 4^ B[ " -"&^ Xe S^ (\\S\\ )Z Q^&^3^2]S\\ A\\S\\ K^&^3^ F^&^4_ >]S" -"\\9^&^3^2]S\\ W^&^3^ 6^ Q] M[ ?` ![1^H]?` =]4](\\ %` >b4c Bb ?`2a .a Ib Pb Aa `0`*^ $^.` <^F]F^F]G`G] F\\S\\ ;b %a2a2a2a2a a:]" -".a !^T_ Bg ` Dd2_8n?m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d Fb@f5a Ad4e-] :f Ra0d AaF\\HaF\\HeJ\\?]._0_0_0_0_2\\U\\0tHh@n?n?n?n?].].].]" -"-h:_J]w " -"P[ 9[/a:aQa7[ Wl \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\ U^1f8c8k;j1`;k7h?n;h9g 5i*b:_8k6kBl=n?l7mD]H]C].].]L_A].`I`H`K]>kAj6kAj9kBuB]H]F]E]E^L_L^" -"R^L^D^I^BrBb7^+b(a D] ;] '] Gd A].].].].] ;] (b:].b #^Q] Dj !a Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f I]K]=]0g7^U^-fC\\S] IfBf6c B[" -"S]5[S].] `K]>k]*]3]W]6^U^._V_;]Wa5]*]2\\V\\6]Wa7^V^ I]*]2\\V\\5^V^2]7]+^V^ @]W\\=v P[ 9\\1c_8m:`R`Cn?n?l9`QaE]H]C].].]M_@].aKaH`K]?`S`Bk8`S`Bk;_R_BuB]H]F]E]D]MaM]P]L]B^K^ArB]1]&])c D] <] '] G] :].].].].] " -";] (^6]*^ #]P^ E^P\\ V^ H^T^4_8n?m:`S`6]:rD]P]P]C`S` Aa :] :a D]&[1^S\\ I^M^=]0^R[7^U^/^R^EZO\\ L^R^ N]U] :],\\0] \\H]B\\H]=\\M]>" -"]._0_0_0_0_0_/uK`R`Cn?n?n?n?].].].]-n@`K]?`S`>`S`>`S`>`S`>`S` H`ScE]H]C]H]C]H]C]H]E^K^@],^T^5],]1\\V\\6\\U`7^V^6]U\\ F],]2\\T\\6^U^=],]2\\U\\6^U^-e9\\U`4],]1\\" -"V\\6\\U`7^V^ H],]1\\V\\5^V^3]6]+^V^ B`1`1`1`1`6]W]>u P[ 9]2e>eUf;^ %q $^O\\ F]1]T],]S];[5]T]N\\@]P[=]*]0]2ZR\\RZ $]2]P]<_W]8]N]\\H\\A\\H\\<\\M\\=]/a2a2a" -"2a2a1_/]V];_M]C].].].].].].].]-]ObBaL]@^M^@^M^@^M^@^M^@^M^ J^N`D]H]C]H]C]H]C]H]E^K^@]-^Q]5].]1\\T\\7\\S]6^V^5c E].]2]S\\7^U^<].]2\\S\\7^U^,a6\\S]2].]1\\T\\7\\S" -"]6^V^ G].]1\\T\\6^V^4]5]+^V^ De6e6e6e6e9\\U\\>u P[ :_3f@gVf<_ &r $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T] &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^ " -" 8ZM^3`P`Ba9]M^=^J\\C]K_B].],^H\\E]H]C].].]O_>].aKaHaL]A^K^D]N^<^K^D]N^>]JZ6]6]H]E]G]C]MaM]O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9] B].] ;] )]4](]" -" %]N]:c6] G] J^P^7a8_1],^K^;c=]H]D]P]P]E^K^ Ee <] \\I]A\\I]<\\N]=]/a2a2a2a2a2a1]U]<" -"^J\\C].].].].].].].]-]K_CaL]A^K^B^K^B^K^B^K^B^K^ K]K^D]H]C]H]C]H]C]H]D^M^?]-]P]4]0]1\\R\\ Ha C]0]2]R] E]0]2\\Q\\ 9c 9]0]1\\R\\ !]0]1\\R\\ ?]4] Di:i:i:i:i" -";\\6]G] P\\ :`5g@gWh>a (_ J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.] T]2]N]5]8ZJ]-]6]X];]-]!^=]L]?]M] *]5_J_Ec:]L^>]H[C]I^C].],]F[E]H]C].].]" -"P_=].]X]M]X]HbM]A]I]D]M]<]I]D]M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_ &] '] H] 9] B].] ;] )]4](] %]N]:d7] F] K]N]8c8^1],]I]>i@]H" -"]D]P]P]E]I] Fg =] =g G]&[2] <]O];]1] 1\\F\\=\\ Q\\F\\ S\\Q\\+]3\\.] IeU\\ M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]" -"B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N] .] '`X_ I] FbWa=bWa=bWa=bWa=bWa<\\6^I^ ?Z2[ :a5gAiXh?c *^ H] 7]1]T]-]S]Aj>]R]Q]@]1]," -"],\\1^X\\X^,] T]3]L]6]'].]7]W];]-]!]<]L]?]M^ +]6^F^F]W]:]K]?]FZC]H^D].]-]DZE]H]C].].]Q_<].]X]M]X]H]X]M]B]G]E]M^>]G]E]M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^" -"-^@]0]']-_S_ '] '] H] 9] B].] ;] )]4](] %]N]:e8_ H] L]M]8]W]7^2]-]G]AmB]H]D]P]P]F]G] Hi >] >i J[3] ;^Q^;]1] 2\\RbT\\Ge R\\VdR\\ T\\" -"Q\\+]4\\2a IfU\\ M\\3\\N\\ ?\\J\\?\\J\\AaM\\ G]W]4]W]4]W]4]W]4]W]4c3^U]=]FZC].].].].].].].]-]H]D]X]M]B]G]D]G]D]G]D]G]D]G]A[H[B]J`E]H]C]H]C]H]C]H]B]O^>g8]N] " -" 1]T_ 3[ 9] G_O^?_O^?_O^?_O^?_O^=\\5]I^ @\\3[ ;c6gAy?d7`8]L]7^7]L]>^ H] 6]1]T]-]S]B_W[U]>]R]R]?]1],],]0d*] T]3]L]6]'].]7\\V];]" -".] ]<]L]@]K] 7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;].]X^O^X]H]X^N]B]G]E]L]>]G]E]L]@]%]6]H]D]I]A]O]W]O]L^T^<^Q^-^?]0]'].^O^ Sb7]U`2b4`U]8a8])`" -"7]T_ M].]%_O_@_2`0`3`/_3c9] )]4](] N_6]N]3^7a/c0_ <^ D[U^ Ga N]L]9]W]6^3]-]G]B`W]W`C]H]D]P]P]F]G] I_X]X_ ?] ?_X]X_ Nb7]2ZFZ=]Q]:]0] 3[SfU[I" -"g R[UfS[ T\\Q\\+]5]2a IfU\\ M\\3\\N\\ ?\\K]?\\K]AaN] G]W]4]W]4]W]4]W]4]W]4]W]3]T]=]/].].].].].].].]-]G]E]X^N]B]G]D]G]D]G]D]G]D]G]B]J]C]KbF]H]C]H]C]H]C]H]B" -"^Q^=j;]P_9b3b3b3b3b3b3bN`Bb3a2a2a2a V_2_2`1`1`1`1` ;aU] :]U` S^T]U^A^L^A^L^A^L^A^L^?]5]I] @^5\\ ]R]R\\>]1],],].`(] U^3]L]6]'].]8]V];].]!^<]L]@]K] :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]C].].]S_:].]W]O]W]H]W]N]C]E]F]L]?]E]F]L]@]%]6]H]D]J^A]O]W]O]" -"L^U^:^S^-^>]0^(]/^M^ Wh:]Wd6f8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](] PdU]A]N]2^8e5g;]Vd?^J^8]6]L] E]V`" -">pA]S]S]:e6kDo>]L]:^W^6^4].]E]D_U]U_D]H]D]P]P]G]E] K_W]W_ @] @_W]W_ Qf9]3\\H\\>^S^:]0_ 6[ThT[K]Q\\ S[T\\R]S[ U]S]+]6],] ?]L]@fU\\ M\\3\\N\\ ?\\K\\>\\K\\;]O\\ G" -"^W^6^W^6^W^6^W^6^W^5]W]4^T]>].].].].].].].].]-]G^F]W]N]C]E]F]E]F]E]F]E]F]E]D_L_E]K]W]F]H]C]H]C]H]C]H]A^S^^K^ O]S]S]B]I]B]I]B]I]B]I]@]5^K^ @]4[ ;f8gAyAg] F] 6]1]T]-\\R\\B]T[6]R]S]>^2]-]*\\.`(] U" -"]2]L]6]'].]9]U];].]!];]L]@]K] =` P`'^7]?\\I]U];]K]@].]F]E].].].]H]C].].]T_9].]W]O]W]H]W^O]C]E]F]L]?]E]F]L]@]%]6]H]C]K]@^P]W]P^K^V^9]S]-^=]/](]0^K^ Xi" -";]Xf9h9fX]h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](] QfW^A]O^1]6f9h;]Xg@_K]7]6]L]=]G]C^Wc@pA]S]S]]L]:]U" -"]5^5].]E]E^S]S^E]H]D]P]P]G]E]@Z+]V]V^-Z4]5ZKZ:]V]V^ Sh9]4^J^>]S]9]._ 8[U_Q[T[L]P\\ S[T\\Q]T[ T]U]*]7]*] @]L]@fU\\ M\\3\\N\\ ?\\L]>\\L]:]Q]:]1]U]6]U]6]U]6]" -"U]6]U]6^W^5]S]>].].].].].].].].]-]F]F]W^O]C]E]F]E]F]E]F]E]F]E]C_N_D]L^W]F]H]C]H]C]H]C]H]@]S];]P_=]S^8i:i:i:i:i:i:iVgIh9h9h9h9h<].].].]'d<]Xg:h9h9h9h9h" -"0^8k?]L]?]L]?]L]?]L]A]K]>]Xf>]K] O]R]R]D]G]D]VZOZV]D]KZV]D]G]A]4]K] @]3[ j=]L]8`7]N]?] F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*] U]3]J]7]']" -".]9\\T];].\\Ua-^;]L]@]K^?].] Uc Pc+_8]>]J]U];]K]@].]F]E].].].]H]C].].]U_8].]W^Q^W]H]V]O]C]E]F]L]?]E]F]L]@^&]6]H]C]K]?]Q^V]Q]I^X^8^U^.^<]/](]1^I^ ]R_h6]L]A]K]?]Q`Q]H^P^?]K]?l4]4](] R^U^W]@]O]0^7g;_S];bT^@`L]8_7]L]>]E]E^W]V]@pA]S]S]" -"=_T_].].].].].].].].]-]F]F]V]O]C]E]F]E]F]E]F]E]F]E]B_P_C]L]V^G]H]C]H]C]H]C]H]@^U^;]N^>]T]6]R_;]R_;]R_;]R_;]R_;]R_;]R" -"_X_T^K_R\\:_S^;_S^;_S^;_S^=].].].]*h=bT^;_T_;_T_;_T_;_T_;_T_1^9_T`>]L]?]L]?]L]?]L]A]K]>aT_?]K] P]Q]R]E]F]E]V\\Q\\W]E]K\\W]E]F]A]4^L] A^@ZN\\ =i8e@yCk?^R^" -"=]L]9b8]O^?] Im B]1]T]5uI]T[6]S^T]<^3]-]*]3^X\\X^,] V^3]J]7](^/]9]T];e7]We/]9]N]?]K^?].] Wd Nd._8]O`U\\T\\K]S]<]L^A]-]F^F].]/]-]H]C].].]V_7].]V]Q" -"]V]H]V^P]D]C]G]L]@]C]G]L]?^']6]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^>`Q^=^Q`?`1]MZ;].]L]A^M^?]Q`Q]" -"G^R^>^M^1^4]4](] D]P^A]R^X]@]P^/]9^Vb=^NZ;`Q^AaN^8_7]L]>]E]F^V]U]>]P]>]S]S]>^P^>`T`7]6]J]<]S]5^6]/]C]G]Q]Q]F]H]D]P]P]H]C]C^&]TZ,^7]7^N^6]TZ H]/^U[TZ9" -"]2n;]U]8]0d <[U]F[M\\P]2[R[ M[S\\P\\S[ Tb(]9]'\\ @]L]@fU\\ M\\3]P]9[R[1\\M\\<\\M\\7\\R\\8]2]S]8]S]8]S]8]S]8]S]7]U]6]R]?]-].].].].].].].]-]F]F]V^P]D]C]H]C]H]C]H]" -"C]H]C]B_R_C]L]T]G]H]C]H]C]H]C]H]?^W^:]M]>]U^6ZM^].].].]+i=`Q^=^P^=^P^=^P^=^P^=^P^2^:^P^>]L]?]L]?]L]?]L]" -"A^M^>`Q^@^M^ P]Q]Q]F]E]F]W^S^W]F]L^W]F]E]B]3]M^ B^B^O[ =k8d?xClA^P^>]L]9]X]8^P]>\\ Hl A] 9uI]T[5]T]T]:^ =]*]5^V\\V^.] V]2]J]7](]/^:]S];h:]Xg0]" -"9^P^?]K^?].]!e Je2_7\\PdW\\S\\L]S]<]M^@]-]E]F].]/]-]H]C].].]X_5].]V]Q]V]H]U^Q]D]C]G]L]@]C]G]M^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])] B]=_O]=].]O_>]N^>].]O_?_" -"O]>].].]S_:]._P`P]M_O]=]N]>_O]=]O_?_1]-].]L]@]M]>]RbR]G^R^=]M]1^3]4](] FaSaD^Qa?]R_.]9]R`>]._O]>^N]8`7]L]>]E]G^U]U^?]P]>]S]S]>]N]>^P^7]6]J]<]S]4^7]/]" -"C]G]Q]Q]F]H]D]P]P]H]C]D_&]&_8]8_N_7] B]/]T[3]1l:^W^8]1]W` >\\U\\E\\N\\P]3\\S\\ N\\S\\P\\S\\ S_']:]&\\ @]L]@fU\\ M\\2\\P\\8\\S\\2\\N]<\\N]7\\S]8]2]S]8]S]8]S]8]S]8]S]8]S]" -"7]R]?]-].].].].].].].]-]E]G]U^Q]D]C]H]C]H]C]H]C]H]C]A_T_B]M]S]G]H]C]H]C]H]C]H]>c9]M^?]U]'].].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]" -"=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]@]M]=_O]?]M] O\\P]Q]F\\D]F\\U^U^V]F\\L^V]F\\D]B]3]M] RuJ`O[ >m9c>wCmA]N]>]L]9]X]7]P]?] Im A] 2\\R\\A]T[5^V^T\\:` ?](\\6]T" -"\\T]/] V]2]J]7])^1_9]S];i;bS^2^8^S_>]K^?].]$e@u@e6_7]QfX\\S\\M^S^=]N^?]-]E]F].]/]-]H]C].].c4].]U]S]U]H]T]Q]D]C]G]M^@]C]G]M]=c-]6]H]B]M]>^R]U]R^G`4c.^:]" -".])] B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T^<^O^2_3]4](] GcUcE]Pa?]Vb-]:]O_?].^N^>]O^8a8]L]?]C]H]T]T]?" -"]P]>]S]S]?]L]@^N^8]6]J]=^S^4^8]/]C]H^Q]Q^G]H]D]P]P]H]C]E_%]%_9]9_L_8] B]0^T[3]0_T_>cWc=]1]U_ ?[U\\C[N]R^4]T] N[R\\Q]R[ 'uG]&] @]L]?eU\\ M\\2]R]8]T]3\\N\\;" -"\\N\\7]S\\7]3^S^:^S^:^S^:^S^:^S^9]S]8^R]?]-].].].].].].].]-]E]G]T]Q]D]C]H]C]H]C]H]C]H]C]@_V_A]N]R]G]H]C]H]C]H]C]H]>c9]L]?]U]'].].].].].]._M]O^/]L]?]L]?]L" -"]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^ P]P]P\\G]C\\G]T^W^T\\G]M^T\\G]C\\B]3^O^ RuJ[X]P[ >o=\\XaX]BwDoC]L\\>]L]:^X^8]P]?" -"] E] 5] 3]S]A^U[4dT];b @](]6ZR\\RZ.] V]2]J]7]*^7d8]R];]R_]-]E]Fm>k=]-rC].].b3].]U]S]U]H]T^R]D]C]G]M]?]C]" -"G]N^^M]?].]M^?]L]>]/]M^?^M]?].].]U_8].^N^N]N^M]?]L]?^M]?]M^?^0]-].]L]@^O^=]S]X]S]D^V^:]O]2_2]4](] H\\U^W]U\\E]Pa?" -"]Vb-];]M^?].^M]>^P]7a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@]L]8]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]F_$]$_:]:_J_9] B]0]S[3]0]P]>o=]2]S_ @[U\\C[M]T_5^U^;u O[R\\R]" -"Q[ 'uH]/ZQ] ?]L]?eU\\ M\\1]T]7^U^4\\O]O]I\\O]T`MZQ]S]O]E]3]Q]:]Q]:]Q]:]Q]:]Q]:^S^9]QmO]-m>m>m>m>].].].]1hL]G]T^R]D]C]H]C]H]C]H]C]H]C]?_X_@]O]Q]G]H]C]H]C]" -"H]C]H]=a8]L]?]U]&].].].].].].^M]O].]L]?]L]?]L]?]L]?].].].].^M]?^M]?]L]?]L]?]L]?]L]?]L] I]Pa?]L]?]L]?]L]?]L]?]O]<^M]?]O] O]P]P\\G]C\\G]ScS\\G]N^S\\G]P]P\\B" -"]2]O] QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\ D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&] W]1]J]7]*^7c8]Q];ZM^=`O^4]4d:]M_?].])d:u:d=_5\\R]O^R\\N]Q]=j<]-]E]F" -"m>k=]-rC].].a2].]U^U^U]H]S]R]D]C]G]N^?]C]G]P_:g3]6]H]A]O]<]S]S]S]E^1_.^8]-]*] A]>^M]?]/^M^?]K]?]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M]@^M^?]/]-].]L]?]" -"O]<]S]X]S]C^X^9]O]2^1]4](]0_IZ O[R\\X]S\\G^O_>]Vd9_U];]L]?].]L]=]P]8]X^9]L]?]C]I^T]S]@]P]>]S]S]?]L]@]L^9]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]G_#]#_;];_H" -"_:] B]0]S[3]0\\N\\>o=]2]Q^ A[U\\C[LcX\\6]T]9u O[RfP[ 'uIf7e >]L]>dU\\<] :f5d4]T]:fT\\O^NfT\\UdOeR\\O^F^3]Q]:]Q]:]Q]:]Q]:]Q]:]Q]:^QmO]-m>m>m>m>].].].]1hL]G]S]R" -"]D]C]H]C]H]C]H]C]H]C]>d?]P^Q]G]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L]@^L] I]Q]X^@]L]?]L]?]L]?]L]?]" -"O]<^M]?]O] O\\WmX]H\\WmX]H\\QaR]H\\N^R]H\\O]P]C]2]O] QuF]R\\ ?qCsDtDrE]L]?]L]:]V]7]R]>x '] 5] 3\\R\\?e3^R\\SbJ^V^O] P](].\\&] W]1]J]7]+^6e:]Q]-^>_M]5^6" -"h<^O` Qe8u8e@^5]R\\M]R\\O^Q^>m?]-]E]Fm>k=]KdFrC].].b3].]T]U]T]H]S^S]D]C]G]P_>]C]Gk6f5]6]H]A^Q^<]S]S]S]F_1_/_8]-]*] A]>]K]A].]K]@]J]?]0]K]?]L]?].].]W_" -"6].]M]M]N]L]@]J]@]K]A]K]?]/^.].]L]?]O]<]T^W]T]C^X^9^Q^3^1]3]']3dN\\ P\\R`Q[G]N_>]Q`;bW];\\K^?]/]L]=]Q^8]W]9]L]?]C]I]S]S]@]P]>]S]S]@]J]B^L^9]6p>^Q^4^9]/]C" -"]H]P]P]G]H]C]Q]Q]G]ViV]H_\"]\"_<]<_F_;] B]1]R[3]1]N]8a6]2]P^ B[U\\C[K`V\\7]T]8u O[RdN[ 'uIf5a <]L]=cU\\<] :f3`1]T];fU\\N^NfU\\T[S]NaQ\\N^G^3^Q^<^Q^<^Q^<^Q^<^Q" -"^;]Q]:]PmO]-m>m>m>m>].].].]1hL]G]S^S]D]C]H]C]H]C]H]C]H]C]=b>]P]P]G]H]C]H]C]H]C]H]<_7]L]?]U_(].].].].].].]K]Q].]J]A]J]A]J]A]J]@].].].].]L]?]L]@]J]A]J]A" -"]J]A]J]A]J] K]P\\V]@]L]?]L]?]L]?]L]?^Q^<]K]@^Q^ O\\WmX]H\\WmX]H\\P_Q]H\\O^Q]H\\O]P]C]2^Q^ D^<]R[ >qDuEsCqD]L]?]L]:]V]7]R]>x '] 5] 3\\R\\=f+]TdL^T^P] P]" -"(].\\2u *]1]J]7],^-_=]P],]>_M]5]7_R^<^Qa Sd .dC^4\\R]M]R\\O]O]>]N_@]-]E]F].]/]KdF]H]C].].]X^4].]T]U]T]H]R]S]D]C]Gk=]C]Gj1c6]6]H]@]Q];^T]S]T^Ga1].^7]-]*" -"] Lh>]K]A].]K]@]J]?]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]._0].]L]>]Q];^U]V]U^Bb7]Q]3^1^3]'^6iS^ P[P^P[G]N_>]N^=dX]<]J]>^1]L]=^R]8^W]9]L]@]A]J]S" -"]S]@]P]>]S]S]@]J]B]J]9]6]J]>]O]5^8]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]]K]@]" -"O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]S]D]C]H]C]H]C]H]C]H]C]<`=]Q]O]G]H]C]H]C]H]C]H];]6]L]?]T_4h9h9h9h9h9h9hK]Q].]J]A]J]A]J]A]J]" -"@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]Q\\U]@]L]?]L]?]L]?]L]>]Q];]K]?]Q] N\\WmX]H\\WmX]H\\P_Q]H\\P^P]H\\O]P]C]1]Q] C]:]S[ ?sEvEqAoC]L]?]L];^V^8^T^>x " -" '] 5] 4]S]]K]A].]K]@p?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?].c4].]L]>]Q]:]U]V]U]@`6^S^4^5b2]&b^Ua<]J]=" -"c7]L]<]S^8]V^:]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?^O^7^7]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]\\I\\@\\O\\X\\J`3^O^>^O^>^O^>^O^>^O^=]O]<^P]?]-].].].].].].].]-]E]G]R^T]D]C]H]C]H]C]H]C]H]C];^<]R]N]G]H]C]H]C]H]C]H];]6]L]?]S`8j;j;j;j;j" -";j;|Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]R]U]@]L]?]L]?]L]?]L]>^S^;]K]?^S^ N\\WmX]H\\WmX]H\\QaR]H\\Q^O]H\\O]P]C]1^S^ D]9]T\\ ?sFwDo?nC]L]?]L];" -"]T]7]T]=] Hj ?] 4]S]8d/]T]T]N^R_R\\ O](] =u Se =]0]J]7].^(]?]O]+]?^K]7]7]L]]K]A].]K]@p?]0]K]?]L]?].].a2].]M]M]N]L]@]J]@]K]A]K]?]-f8].]L]>^S^:]U]V]U]?^4]S]4^4`0]$`<^Si O[O" -"\\O\\H]N^=]M^@^S`<]J]=c7]L]<]S]8^U]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]7]6]/^E^H]P]P]G]H]A]S]S]E]C]Iz<]]M]>]M]>]M]>]M]>^O^=]O]?]-].].].].].].].]-]E]G]Q]T]D]C]H]C]H]C]H]C]H]C]<`=]S]M]G]H]C]H]C]H]" -"C]H];]6]M^?]R`;l=l=l=l=l=l=~Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]S]T]@]L]?]L]?]L]?]L]=]S]:]K]>]S] M]P]P\\G]C\\G]ScS\\G]S^N\\G]P]P\\B]0]S] D]" -"7\\T[ >sFwCn?mB]L]?]L];]T]7]T]=] Hi >] 4]S]7[Xa1]T^T^O]P_T] O](] =u Se =]0]J]7]/^'^A]N]+]?^K]7]8^L^]K]A].]K]@p?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?]+e9].]L]=]S]9]V]T]" -"V]@_4]S]5_4b2]&b<\\Nd M[O]P\\H]N^=]L]@]Q_<]J]?e7]L];]T]8]T]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]8^6].]E]G]P]Q^G]H]A^T]T^E]C]Iz<]]M]>]M]>]M]>]M]>]M]>^O]?]-].].].].].].].]-]E]G]Q^U]D]C]H]C]H]C]H]C]" -"H]C]=b>]T]L]G]H]C]H]C]H]C]H];]6]M]>]Qa>`P]>`P]>`P]>`P]>`P]>`P]>`PoQ].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]T]S]@]L]?]L]?]L]?]L]=]S]:]K]>]S] " -"L\\P]P\\F\\C\\F\\T^W^T\\F\\T^M\\F\\C\\B]0]S] E^7]U[ >sFwBl=kA]L]?]L]<^T^8^V^=] Ij >] ]K]A].]K]@],]0]K]?]L]?].].c4].]M]M]N]" -"L]@]J]@]K]A]K]?](d;].]L]=]S]9^W]T]W^@`5^U^5^/_3]'_8ZJ` K[O]P\\H]N^=]L]@]P];]J]@_0]L];]U^9^T^;]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]@^M^:^5].]E]F]Q]Q]F" -"]H]@^U]U^C]E]G_\"]\"_BZT]TZB_F_;] B]1]R[3]1\\L\\?o I_S] A[U]F[ V]T] W] N[S\\R]R[ S] ]L]6\\U\\ ']T]/\\O\\V\\@\\H\\A\\O\\V\\M_0o@o@o@o@o?m>l>].].].].].].].].]-]F^" -"G]P]U]C]E]F]E]F]E]F]E]F]E]=d?^V]L]F]H]C]H]C]H]C]H];]6]N^>]O`?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?" -"]L]?]L]=^U^:]K]>^U^ L\\P]Q]F\\D]F\\U^U^V]F\\U^M]F\\D]B\\/^U^ OuD]V[ =sFwBk;i@]L]?]L]<]R]7]V];] F^ Nu=[T^3]S]R]O]N_V\\ N](] 1] ].]L]6]1_%]Aq0]>]K]" -"8]7]J]/] Md:u:d>]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]S^9].]RaR]H]P^V]C]E]F].]E]F]M],]8]6]H]>]U^8]W^Q^W]H^U^4]2^3]+],] R^M]>]K]A].]K]@],]0]K]?]L]?" -"].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]$`;].]L]=^U^8]W]T]W]@b5]U]5^,]3]'] J\\Q_Q[G]N^=]L]A]O];]J]@].]L];]U]8]R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];" -"^4].^G^F]Q]Q]F]H]?_W]W_B]E]F_#]#_B\\U]U\\B_H_A\\U]U[ H]1]R[3]1]N]?o H`V] @[T]G[ U]T] X] N[S\\Q]S[ S] ]L]6\\U\\ (]T]/]P\\U\\A]I]B]P\\U\\M^/o@o@o@o@o@o@m>].]" -".].].].].].].]-]F]F]P^V]C]E]F]E]F]E]F]E]F]E]>_X_?]W^L]F]H]C]H]C]H]C]H];]6]P_=]M^@^M]?^M]?^M]?^M]?^M]?^M]?^M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]" -"A]J] K]U\\Q]@]L]?]L]?]L]?]L]<]U]9]K]=]U] K]Q]Q]F]E]F]W^S^W]F]W^L]F]E]B\\.]U] NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];] E] Nu=[S]3\\R]R]O]M_X\\ M](" -"] 1] ].]L]6]2_$]Aq0]>]K]8]7]J]/] Ke=u=e<]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]R^:].]RaR]H]O^W]C]E]F].]E]F]M^-]8]6]H]>]U]7]W]O]W]I^S^5]3^2]+],] R" -"]L]>]K]A].]K]@],]0]K]?]L]?].].]W_6].]M]M]N]L]@]J]@]K]A]K]?]\"_<].]L]<]U]7]W]T]W]Ac5^W^6^+^4](] H[R\\X]S\\G]N^=]L]A]O];]J]A^.]L]:]W^9^R];]L]@]O]O]J]S]S]@" -"]P]>]S]S]@]J]B]J]9]5]L]?]K];^4]-]G]D]R]R]E]H]>kA]E]E_$]$_B^V]V^B_J_A^V]V] I]1]R[3]0\\N\\>o G`X] ?\\U_Q[T\\ T]T] ] N\\T\\Q]T\\ S] ]L]6\\U\\ )]T].\\P\\T\\A\\I]A" -"\\P\\T\\N^.o@o@o@o@o@o@m>].].].].].].].].]-]F]F]O^W]C]E]F]E]F]E]F]E]F]E]?_V_@]W]K]F]H]C]H]C]H]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/" -"]J]@]L]@]J]A]J]A]J]A]J]A]J] K]V\\P]@]L]?]L]?]L]?]L]<^W^9]K]=^W^ J]R]R]D]G]D]W\\Q\\W]D]W\\L]D]G]A\\.^V] NuC]W[ ]K]9]6]J]/] He@u@e H\\R]M]T]Q^J]A]J]@]/]G^E].]-]F]F]H]C].].]Q^;].]Q_Q]H]N]W]B]G]E]-]G^F]L]-]8]6]I^>^W^7]" -"W]O]W]I^R^6]4^1]+],] R]M^>^M^@]/^M^?]-]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M^A^M^?] ]<].]L]<]U]7]X]R]X]B^W^5]W]6^)]4](] H\\T]W]U\\F]O_=]L]A]P^;^L^A]-]L" -"]:]W]8]P]<]L]@]O]O]J^T]T]?]P]>]S]S]@^L]A^L]8]5]L]@^J]=^3]-^I^D^S]S^E]H]]G]C_%]%_A_W]W_A_L_@_W]W_ J]0]S[3]0]P]5]4],b =[ThT[ R]T]!] M[T\\P]U[ R] ]L" -"]6\\U\\ *]T].]P[S\\B]J]A]P[S\\N].^J]B^J]B^J]B^J]B^J]B^K^A]M]=]/].].].].].].].]-]G^F]N]W]B]G]D]G]D]G]D]G]D]G]?_T_AbK]E]I^C]I^C]I^C]I^;]6j;]K]A]M^?]M^?]M^" -"?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8^M^<]W] I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W] MuC]X[ ;cWZWbDe" -"WZXe>e6e>]L]?]L]=]P]8^X^:] F^ H\\R\\5[S]5]Q]R]O^L` K]*] 0] !^.]L]6]4_\"]2],^>^M]8]6]J]0] DeCuCe E]R\\M]T\\P]I]A]J]@]/]G]D].]-]F]F]H]C].].]P^<].]Q" -"_Q]H]N^X]B]G]E]-]G]E]L^.]8]5]J]<]W]6^X]O]X^J^Q^6]5^0]+^-] R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]" -"(] GcUcE]P_=]L]A]P]9]L]@]-]L]:^X]9^P]<]M^@]P^O]I]T]T]?]P]>]S]S]@^L]@]L]8]5]M]?]I]>^2],]I]B_U]U_D]H]:c<]G]B_&]&_?_X]X_?_N_>_X]X_ I]0]S[3]0_T_5]4]+` ;[" -"SfU[ P^U^#] L[U\\P]V[ Q] ]M^6\\U\\ ,^U^-\\P\\S\\B\\J]@\\P\\S\\N].]I]B]I]B]I]B]I]B]I]B]I]B^M]=]/].].].].].].].]-]G]E]N^X]B]G]D]G]D]G]D]G]D]G]@_R_A`J]D]J]A]J" -"]A]J]A]J]:]6g8]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?].].].].].].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;aP]?]M^?]M^?]M^?]M^;]W]8^M];]W] H]S]T^B]J^B]J^B]J^B]J^@" -"\\-]W] G^1_ :aW[V`BcW[Wc]N]<]P]7]X]8] F]KZ X]S]5[S]5\\P]R]N]K_ K]*] 0] !],]N]5]5_\"]1],]<]M]9^6^L^0] Ad Nd A\\R]O^U\\P^I^B]K^?]H[C]H^D]" -".],]G]F]H]C].].]O^=].]P^Q]H]M]X]A]I]D],]I^E]K]AZH^8]5]J]<]W]5bObJ^O^7]6_0]*]-] R]M^>^M]?^/]M^?^.]/]M^?]L]?].].]T_9].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^;" -"]W]5aRaB^U^6c8_(]4](] FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?\\P_Q]H]T]T]?]P]=]T]T]?^L]@]L]8]4]N]@^I^?]1],^K^A`W]W`C]H]7]8]I]@^&]&^=i=^N^^P^=^P]7]X]8_ H^M[ F] 6]S]>ZQ[T^6]P]S^N^K^ K]*] 0]:] 8]0],]O^5]6_2ZI]1]-^<^O^9]4]L]0]<].] Uc Pc1]2\\Q^S`W^P]G]B]K]" -">^J\\C]I^C].],^H]F]H]C].].]N^>].]C]H]MbA^K^D],^K^D]K^B[I]7]5^L^_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-" -"]O_;]X^5aRaC^S^6a8_']4](] D]P^B^Ra>^N]@]Q]7]N]?^.]L]9a8]N]=^N^?]Q_Q]G]U]U]>]P]=]T]T]?_N]>]N]7]4^P^@]G]@^1]+^M^?mB]H]7]8^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]" -"4]'^ <\\F\\ M\\S\\ J\\F\\ L^N^6\\U\\ ,\\S\\-]OhG]K]@]OhQ]LZ=]G]D]G]D]G]D]G]D]G]D]G]D^L]<^J\\C].].].].].].].]-]J_D]MbA^K^B^K^B^K^B^K^B^K^A_N_B^K]B^L^A^L^A^" -"L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^;_O]=]O_>]O_>]O_>]O_:a7_O]9a E^P_>^P_>^P_>^P_>^P_>\\,a H^.]" -" /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ H^O\\ F] 6\\R\\=[R[U^5\\N]T]L^M` L]*] 0]:] 8]1^+]P]4]7_1[L_1]ZM];].] R` P`.]2]QfXaN]G]B]L^=^L]C]K_B].]+" -"_J]F]H]C].].]M^?].]C]H]La@^M^C]+^M^C]J]B]L^7]4^N^:a4aMaK^M^8]7^.]*^.] Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`" -"P`D^Q^7a8^&]4](] S]Sb>_P^@]R^7^P^>^MZ<]L]9a9]M]=_P`XZB]Q_Q]G^V]V^>]P]=^U]U^?`P^>^P^6]4]Q^?]G]A^0]*^O^]P`>]P`>]P`>]P`>]P`>]P]X^LZN^NZ;_LZ>_LZ>_LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a Dk],a " -"H]-] /[,[._0_;]L]=j<]N]7`5a J_S^ F] 6\\R\\=^U[W_5]N^V^K_Rd L],] /]:] 8]1])^T^3]8_0^Q`0]<]Q_8^S^8^3_R_=]R^:].] O] P]+]1\\PdW`N^G^C]N_;`R`C]NaA].]*`O" -"`F]H]C].].]L^@].]C]H]La?`S`B]*`S`B]J]B`Q_6]3_R_9a4aMaL^K^9]8^-])].] Q_Tb>aS^;_R\\:^Sa=`Q]>]-^Sa?]L]?].].]P^<].]M]M]N]L]=_T_=aS^;^Sa?]/^R_:]-^Sa:a3_P_" -"C^P^7_8^%]4](] S_V^X^?aS^>]T^5_T_=`R]<]L]8_8]M^>`SdA]SaS]E^W]W^=]P^=_W]W_>]X]T_<_T_5^4^T^?^G^C^/])^Q^8c=]H]7]6`S` ?] ;c >c E]._W[V\\9]4^J^9]4]%] ;]L]" -" IZQZ H]L] !u ,`Sd9\\U\\ ,ZQZ,]E\\E]L]?]E\\M_S^>^G^F^G^F^G^F^G^F^G^F^G^F^K]:`R`C].].].].].].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6" -"].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_Tb>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6aS^7_ Bi:i:i:i:i=]+` I],] /[" -",[-].]:]L]]C]H]K`>kA])kA]J^Cm5" -"]2j7_2`M`K^J]9]8tC])].] PgX]>]Xf9h9fX]],fX]?]L]?].].]O^=].]M]M]N]L]qA^U]W]U^D" -"i<]O`?k=]Xg:h3a7f>uCn?]/eSe;]:]H]7]5k >] :a n?\\H\\8]4]%] 9^R^ *^R^ Xu ,q9\\U\\ /]D\\F]LfH]D\\Li>]E]F]E]F]E]F]E]F]E]F]E]F]JnIkBn?n?n?n?].].]." -"]-n@]K`>ki-]]C]H]K`]Wd6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](] QdV`B]Xe;d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O" -"`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] 7f &f Vu ,]XdW_9\\U\\ /\\C\\F\\KfH\\C\\Kg=]E]F]E]F]E]F]E]F]E]F]E]F]JnHh@n?n?n?n?].].].]-l>" -"]K`]C]H]J_9a<]$d?]I^?c0].b3_2" -"_K_M^G^;]8tC](]/] M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]M]N]L]8`8]U`3`U]?],c2a0_T]9_2^N^F^K^8]7m4]4](] O`R^B]Va8b-`3d:]L]7]9^J]?]V`T]>cUc?c9]N_:" -"a8]T`3`-_4`X IX *W FW " -" " -" " -" HX W 4Z 3VCT X W 4Z " -" HX W 4Z 'VCT ;X W 3Y 2UCT KX W 3Y 0W " -" " -" " -" @W !W 4\\ 5YET ?XHX 8] >W !W 4\\ 7XGX KW !W 4\\ 7XHX +YET :W !W 3[ 5ZFT ?XGX EW !W 3[ 7XGX 5W " -" " -" " -" >W \"V 3\\ 7]HU ?XHX 9` ?W \"" -"V 3\\ 7XGX JW \"V 3\\ 7XHX -]HU 9W \"V 3] 7]HT ?XGX DW \"V 3] 8XGX 5V " -" " -" " -" W $V 3VNV 8XGX IW $V 3VNV 8XHX -_KV 8W $V 2] 7_KU ?XGX CW $V " -"2] 8XGX 6V " -" " -" :W &W " -"4VLV :j >XHX :VJV >W &W 4VLV 9XGX HW &W 4VLV 9XHX .j 6W &W 3VMV 9i >XGX BW &W 3VMV 9XGX 7W MW " -" " -" " -" CV 'W 4VJV ;j >XHX ;UGV >V 'W 4VJV :XGX GV 'W 4VJV :XHX .j" -" 5V 'W 3VKV :i >XGX AV 'W 3VKV :XGX 8W N[ " -" " -" " -" DV )W 4VHU TEY ;XHX V ,V 2UEU TCU :XGX =U -V 2UCU =XGX ;V NV" -"IV \"W " -" " -" JU /V 3VBV ETBT :U /" -"V 3VBV FU /V 3VBV (U /V 2UAU DU /V 2UAU @V NVGV " -" $X " -" *X " -" JX GTBT MX GX 7V :UEU DX GX 7V " -" JX GX 7W 4X GX 6V GX GX 5V (X &X " -" )X 8V " -" ;X FTBT " -" LX IX 7X W E\\ AW ,W ,W ,W ,W " -" HY GV +Y 4Z NX @X %W " -" DUDU =Y 7W KW 6Z 4XDT BTAT BW KW 6Z IW KW 6[ ,Y )XDT AW KW 5Z 4XDT " -" KW KW 4Z ,W BW 8V (S W H_ AW ,W ,W ,W ,W L] GV +] ;a " -" #[ F^ 8XGX +W BTEU " -" *R 9a :W MW 6\\ 6ZET ?XHX W Ja AW ,W ,W ,W ,W N_ GV +_ " -"?e 8] J] Jb 8[ <[ $Y FY 7XGX =Z Di 5W 8Z .Y !W FW *Y 4W)V*W)V-Y(V " -" W $a MY " -" EW 5W >W Kb AW ,W ,W ,W ,W !a GV +a Ch =f ^ Mf 2Z @x Mx a 5a &W 0g #\\ -_ <\\*V.\\*V0a-V\"X )Z /Z /Z /Z /Z 4WJV 1~U+d Kx Mx Mx Mx MX -X -X -X ,j" -" @[3X Dc 8c 8c 8c 8c W \"W 4VNV 8]HU ?XHX " -"BW \"W 3VNV 8XHX 2W ?W &XHX ^ K~\\ >S 3Q +[ @[;[ ;Q ;e HX 2VFV #VBV FS 6`1V#g GV !V 3V !T 7W 0d" -" :` ;j ?k -[ Dq :g Ky Df ;d $f 1Z @o 5j Np Ex Mt :m\"X/X'X -X -X3Z%X -]0]0\\4X Gi Lm 4i Ln ;m#~W$X/X-X(X-X4Y4XCY1Y-Y.Y&~S%a >W $a N[ EV " -"5W >W Lc AW ,W ,W ,W ,W \"b GV +a Dk Aj \"_ h 3Z @x Mx ?i 6X C~Q)X?X?X Ni 6V /V /" -"V DX &f #W0W e >XGX %c#e +b\"i 9_ Be 9d 'V 3k %^ /c @^*V0^*V2d.V\"X )Z /Z /Z /Z /Z 3b 1~U.j Nx Mx Mx Mx MX -X -X -X ,p F\\4X Gi >i " -">i >i >i BiEV.X/X'X/X'X/X'X/X.Y.Y#X 'j ;V \"V 5VLV :_IT >XHX V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT XHX AV \"V 3VLV 9" -"XHX 2V >W &XHX !_ K~[ >T 4R -_ D_?_ >S =t Fh IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T 7V 0f @e >o Co 0" -"\\ Dq W M" -"d AW ,W ,W ,W ,W HW 1b GV +b Fm Dm #` \"j 4Z @x Mx Am 8X C~Q)X?X?X!m 9X 0V 0X EX 'h" -" $W0W \"h ?XGX 'g%g 0h%i :a Cf :f *V 4m %^ 0e A^+V/^+V1f1V!X )Z /Z /Z /Z /Z 2` 1~V0o\"x Mx Mx Mx MX -X -X -X ,t J\\4X Im Bm Bm Bm Bm F" -"mHV-X/X'X/X'X/X'X/X-X.X\"X (l ;V $V 4UJU :ULXLU >XHX XHX @V $V 2UJU 9XHX 3V" -" =W &XHX !` K~Z >T 4S /a FaAa @T @w Hl KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T 7V 2j Eh ?q Dp 1\\ Dq >" -"l Ly Hn Bj +l %e E\\ At >s$v Kx Mt >u&X/X'X -X -X5Z#X -^2^0]5X Jo q ;o r Br%~W$X/X-X(X,X6[6XAY3Y+Y0Y%~S%W 3V IW !_ FW 7W >W Md AW " -",W ,W ,W ,W HW 2[ ?V #[ Hn En #` #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o ;Z 1V 1Z FX KS 0i #W2" -"W LV ,i ?XGX *l'h 3l'i ;c Dg ;g ,W 6o %^ 1g B^,V.^,V0g3V X *\\ 1\\ 1\\ 1\\ 1\\ 2^ 0~V2s$x Mx Mx Mx MX -X -X -X ,v L]5X Jo Do Do Do Do HpKW" -"-X/X'X/X'X/X'X/X-Y0Y\"X )n XHX ;UEU XHX @W &W 3VJV :XHX 4W =W &XHX " -" 1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y =S 4U 1c IdCc AU Dz In LX 2VFV $VBV ES 9g7V$k HV #W 1W #T 8W 3l Fh ?r Eq 3] Dq ?m L" -"y Ip Em -n )k H\\ Au Av%x Mx Mt ?x(X/X'X -X -X6Z\"X -^2^0]5X Ls\"s ?s\"s Et%~W$X/X,X*X+X6[6X@Y5Y)Y2Y$~S%W 3W JW \"a FW 8W >W NZ 6W ,W " -",W ,W ,W HW 2X \\ 2V 2\\ GX KS 1j #" -"W2W LV -j ?XGX +ZEZ)VGY 5ZDZ)i T 5V 2e KfEe CW G| Jp MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T " -" 8V 3n Gh ?s Fr 5^ Dq @n Lx Ir Go .o -q L^ Bv Cx&z x Mt A{)X/X'X -X -X7Z!X -^2^0^6X Mu#t Au#t Gu%~W$X/X,X*X+X6[6X?X5X'X2X#~S%W 2V JW #c FW" -" 9W >W NX 4W ,W ,W ,W ,W HW 2W ;V NW IZCY Hp JY &ZDZ 9^ Bx Mx Eu W *W 2UFU ;XHX 6W ;W &XHX 7h =h =h =h =h DWJV K~X >T 5W 4g MgFg EY J~ K]FZ MX 2VFV $VBV " -"ES :XGX9V%\\GX HV $W /W 3PATAP GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[ /[H] 0u N^ Bw E_D^&{!x Mt B`C_)X/X'X -X -X8Z X -_4_0_7X N^" -"E^$u C^E^$u H^E\\%~W$X/X,Y,Y*W7]8X>Y7Y'Y4Y#~S%W 2V JW $e FV 9W >W NW 3W ,W ,W ,W ,W HW 2W ;V NW IY@X >X " -"4[AV IX &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X&^E^ B` 4V 4` IX KS 3\\GW \"W4W KV .YBT ?XGX .V7V,P=W :W8W /VEV 3V +V /V " -" 7eGU KU 3WCW ;U-V$U-V LV5V NX +^ 3^ 3^ 3^ 3^ 3^ 1~W6_D^&x Mx Mx Mx MX -X -X -X ,{\"_7X N^E^ L^E^ L^E^ L^E^ L^E^ !^Ed*X/X'X/X'X/X'X/X+Y4Y X +Y?" -"X ;V *V 4UDU >TEZ TEZ T 5Y 5g MhHi G[ M~Q L\\AW MX 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B" -"[ I[CZ 0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X?] J[=X =X W X 3W 4W ,W HW 3X ;V NX KY?X Ca 9Y:R HX (X>X :VNV BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\ " -" Db 5V 5b JX KS 3ZBT !W6W JV .X?R 4V4U HV ;V4V 1VCV 4V *U 0V 7fGU KU 4WAW TDX ;a 6V ,V 4UBU GV ,V 3UCU 0` 6TDX 4V ,V" -" 2UDU >TDX >V ,V 1UDU :V 9W (o Do Do Do Do GWIU J~V >T 6Z 6i jIj I\\ N~R M[=U MX 2VFV %VBV H] AWCW;V%Y=R" -" HV %W -V 4UETEU IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R -X JZ>Y JZ?Y 1U>Z 5`C_#` CX;[ H[7W&X9_$X -X (\\6X)X/X'X -X -X;[ MX -_4_0`8X![;[&X" -"=[ F[;[&X<[ LZ8U =X W W 2W 4W ,W HW 3W :V MW KX=W Cc " -";X7P HX (WR !X8X JV /X

}n$4Sa_! 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

$TfqOb{o`%z9}c&h{o^hm@3(i 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

`ski^5b?^M(ZuB5EW`bXWg@S0X6asY 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

6kjHM@R_g>nw6;H%V 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

W W " -" 2W 4W ,W HW 3W :V MW KWU.U 4VAV &V 5U *U 2V 6gGU KU 5W?W =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5WNW 5WNW 5WNW 4XHX H[4U&X -X -" -"X -X -X -X -X -X ,X6]&`8X\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z 'Z8['X/X'X/X'X/X'X/X)Y8Y MX ,W:W 9V 0V 3U@U ?[ 1V 0V 3U@V GV 0V 3U?U 8h 1V 0V 2U@U " -" CV 0V 1U@U >V 7W *`L` I`L` I`L` I`L` I`L` JV =X,X >T 6] 9k\"lKl K_ #\\ 'Y8S MX 2VFV %VBV Nk IVAV=V$X 1V %V +V " -"6YHTHY -V EW 5Y>Y :X ?R5Z .Y ;VMX DX +Y DX IYW W 2W 4W ,W HW 3W :V MW KW;W De =W " -" -X *W:W V$X 1V &W +W 5XITIX +V EV 4X[ JX -XNW8WNX0a9X#Y3Y(X9Y JY3Y(X9Y NX LX W W 2W 4W ,W HW " -" 3W :V MW LX;W Df >W ,W +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y Kj 9V 9j AS 5X 8W:W HV /W #T)T KV " -" @T(T 6U?U &V 5T +V AhGU KU 5V=V =U0V!U0V JV7V WLW 7WLW 7WLW 7WLW 7WLW 7XNX 6XGX IY.R&X -X -X -X -X -X -X -X ,X2Z'a9X#Y3Y%Y3Y%Y3Y%Y3Y%Y3" -"Y )Y3Z)X/X'X/X'X/X'X/X'X:X Ki >W8V *XHZ FW ,ZW W " -" 2W 4W ,W HW 3W :V MW LW:W Dg ?W ,X ,W8W >WLW ?Y 3X +Y1Y\"v#X0X)X?X?X+Y1Y MYNVNY :V :" -"YNVNY BS 5X 8XU1V U1V KW7V NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X " -"-X -X -X -X -X -X ,X1Z(XNX:X$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y P)P$Y3[)X/X'X/X'X/X'X/X'YVKX DX -X BX IX8X NX7W KP 1P =X Y *Z W 0W MW +ZAZ 0W >W W 2W 4W ,W HW 3W :V MW LW:W DSF[ @X -X " -" -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)X?X?X,Y/Y YMVMY ;V ;YMVMY CS 5X 5P*Q JWU2V NU2V$_7V NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X -X -X -X -X -X -X ,X0Z)XNX:X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y\"R+R&Y3]*X/X'X/X'X/X'X/X&Y>Y Jp EW:Y " -" +R@Y 7Q 2W .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y NV ;X/X 0V 5T 8c ^ AW4W ?Z >W6W KY " -" \"Y 0X 2VFV &VCW#[LSKZ KV?V@V\"W 0V 'W )W 1XNTNX &V FW 6Y:Y X *Z NW 0W MW ,Z?Z 1W >W W 2W 4W ,W H" -"W 3W :V MW LW:W DPAY ?Y .W -W6W @WJW >Y 5X ,X-X&_MXM_&X0X)X?X?X,Y/Y !YLVLY " -"W FV /X 'TCfFT2i CUGfBT 9U?U &V 7U 5] >iGU KU 6V;V >U2V NU2V$]5V NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX KY /X -X -X -X -X -X -X -X ,X" -"/Y)XMX;X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y#T-T'Y3]*X/X'X/X'X/X'X/X%X>X Ir GW=\\ GY 9S 3W /XDVDX$X2X$X2X$X" -"2X$X2X V ;X0X 0X 7T 8d X$X-WJW EX6X Y .X.Y)X -X -Y .X/X'X -X -XBZ EX -XLV:VLX0XMX;X&Y-Y+X7X NY-Y+X7X!X KX Z W FV .X (TDgFT3j CTFhDT 9U?U &V 8U 4\\ =iGU KU 6V" -";V >U3V MU3V#\\5V MWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX LY .X -X -X -X -X -X -X -X ,X.Y*XMX;X&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y%V/V)Y3_+X/X'X/X'X/X'X/X%Y@Y Is HW?^ " -"?Z /Z /Z /Z /Z /Z /Z6Y NZ 0Z /Z /Z /Z 8Y 1Y 3Z /Z /Z /Z /Z 3ZCV 5WDX DXCVCW%X0W%X0W%X0W%X0W V :X1X 0X 7T 9f =k#~`\"h Cf " -"EW4W @\\ ?X8X LX !Y /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V GW 5X8X W\"W.XJX" -" FX6X X -X.Y)X -X -X -X/X'X -X -XCZ DX -XLV:VLX0XLX^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm ^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X $YJVJY >V >YJVJY Ma =X 7V0V JW@W EV .Y *TEiET5k DTEiDT :VAV &V 9U 3_ ;W6W NiGU " -"KU 6V;V >U3V MU3V#_8V NXJX ;XJX ;XJX ;XJX ;XJX ;XJX :XEX LX -X -X -X -X -X -X -X -X ,X.Y*XLXa'b 7` 5` 5` 5` AW ,W ,W ,W DY EWG_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X NWBVBW&W.W&WJP:PJW&W4PJW&W." -"W!V :X2X 0X 6S 8g >k#~`#j Fj GW4W @\\ >W8W LX X .X 2VFV 'VBV$XGSCR KV?VBV X 1V (W 'W ,\\ V GW 5X8X f CWIb =bIW MWI^ =j Im U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X -X -X -X ,X-Y+XKWf ;f ;f ;f ;f +Z >eJU NW6W MW6W MW6W MW6W\"W" -"2W MWIb IW2W NWAVAW(W,W(WJRU5V KU5V GXTKW)W4TKW)W+W\"V 9X3X 2X 5T :k ?i\"~`$m Jn IW4W A^ ?X:X MW " -" NY .X 2VFV 7~X2XFS VIV>X2YIY DYFY +Z JW .V NW 1Y3Y 1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW " -",WKfGg8WKg Cl FWLh ChLW MWK` @m Im Y =W6W JW-W&YJb }!WCWCW Hk Dx&{ W4W CWFW P JSCVAVDS :WEV $V W6W NiGU KU 6V" -";V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX W:X MW NX -X 2VFV 7~X2WES WKX0XJX>X(Y)X,X7X!Y)X,X7X!Y LX VIV>X1YKY BXFX +Z IW .W " -" W 2Y1Y 2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,WW6W NiGU KU 6V;V BQ?Q 0U6V JU6V BU>V EX 0WFW =WFW =WFW =WFW =WFW =WFW X(Y)X.Y)X.Y)X.Y)X.Y)X%Z9Z*Y6WJX,X/X'X/X'X/X'X/X!XFX EX;Z LWDX ?o Do Do Do Do Do DoKn4n Cn Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6" -"W MW6W MW6W!W4W LWMj LW4W W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X 2X 5T ;n ?g!~_%p LZDZ JW4W A^ >W:W MW MX -X 2VFV 7~X2WES VJX0XIW>X(X" -"'X-X7X!X'X-X7X!Y LX VIV>X1YKY AXHX +Z HW -V W 3Y/Y 3p FWMk Fo EkMW Io Nl 8kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im \\>W0X=X LW5X u 6W :V MW EkJV Wj Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X.YBXBY+X0X)X?X?X/X'X#T HV IT " -":V ;T3T :V CV +o BX 6ZM`MZ GXFX CV *\\ 3SFW,S:V>V 0R@R KSBV@VDS 9e #V ?W \"V ?W6W NiGU KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX" -" ?XFX ?XFW =XCX NX +X -X -X -X -X -X -X -X ,X+X,XIW>X(X'X/X'X/X'X/X'X/X'X%Z;Z)X5VHX-X/X'X/X'X/X'X/X XHX DX:Y LWEX >p Ep Ep Ep Ep Ep EpMp6o Do Do Do Do" -" HW ,W ,W ,W 'o IWMk Gp Ep Ep Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W 2X 4T ;o @g ~^%q NY@Y KW4W B`" -" ?XX -XJW@WJX0XIX?X(X'X-X7X!X'X-X8Y Y MX W/YMY @YJY +Y GW -V W 4X+X 4YE\\ FWNXG\\ H]EX F\\GXNW J\\F[ " -"GW ,\\GXNW MWNXG[ JW ,W ,W?Z GW ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V MW 9X=X\"[IZKW W=Y /W @m H]DV " -"CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y XCX NX +X -X -X -X -X -X -X -X ,X+X,XIX?" -"X(X'X/X'X/X'X/X'X/X'X$Z=Z(X6WHX-X/X'X/X'X/X'X/X YJY DX9Y MWEW =YE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE]N\\G[7]EX E\\F[ F\\F[ F\\F[ F\\F[ IW ,W ,W ,W (p IWNXG[ H]H" -"] G]H] G]H] G]H] G]H] 1Z E]H^ JW6W MW6W MW6W MW6W W6W KWNXG\\ MW6W NV>V>V,V&V,VJZFYIV,V6YIV,V&V%W 7W6X 3X LR:T ;q @e N~^&s!Y>Y LW4W B` >WXJX +Z GW -W !W 5X)X 5U>Z G_CZ I[>T FZC_ KZAZ HW -ZB_ " -"M^BZ KW ,W ,W@Z FW ,^CZMVCZ;^BZ IZBZ I_CZ IZC_ M^ 5YY .W AXJa IZW2W EWDW 9Y =X /X'X/YAXAY,X0X)X?X?X/X'X%X JV KX Z FU>Z FU>Z FU>Z FU>Z FU>Z FU>eBZ9[>T FZAZ HZAZ HZAZ HZAZ JW ,W ,W ,W )r J^BZ IZBZ GZBZ GZBZ GZBZ" -" GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X 4X NU:T WX !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X Ee Le 3Z ?U=bKUCU6XDX IX9Y X +X+X+X -X /X +X/X" -"'X -X -XL[ Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V MW =_C_(YBXLV NW?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y " -"L[B[ ;W >W2W FWBW 9Y >X 0X%X0X@X@X,X0X)X?X?X/X'X&Y JV KY =V >Y7Y =V CV .[HSFR BX 3t BWHW AV .WN\\ 9SFV)S;V?W 3UCU LSAV@VCS 7_ V BV LU ?W" -"6W MhGU KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AXDX AXDX AXDX @XBX NX +X -X -X -X -X -X -X -X ,X+X,XHX@X(X'X/X'X/X'X/X'X/X'X\"ZAZ&X8WFX-X/X'" -"X/X'X/X'X/X MXLX BX8X MWFW Y;Z:R GY=Y JY=Y JY=Y JY=Y KW ,W ,W ,W *]E[ J]@Y JY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y JW6W MW" -"6W MW6W MW6W W7X K]?Y NW7X V=V=U-V$U-VGZJYFU-V7YFU-V$U%W 7X8X &~X/X:T =t @c L~\\'v\"W:W LW4W CXNX ?X>X MV $x EX 2~X2WES :VDW" -"EV FZ :W #W 7XKTKX )V IV 4X4X >X !X 0Y BWDX Dm FXKf /Y AYBY KX5Y MX -X Gd ~X d 5Y ?V>dLUCU6WBW IX;Z Y +X+Y,X -X 0Y +X/X'X -X -XM[ ;X -XIWBWIX" -"0XGW@X)Y'Y.X8X!Y'Y.X9Y M] #X aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWCW MXZ W2W FWBW 9Z ?X" -" 0X%X0X@X@X,X0X(X@X@X/Y'Y(Y IV JY >V ?Y5Y >V CV .YFSDP BX 2q @XJX AV /WK[ :SFV)S;V@X 4VDV LSAV@VCS 6\\ MV CV KU ?W6W MhGU KU 4V?V @V" -"DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW@X)Y'Y1Y'Y1Y'Y1Y'Y1Y'Y\"ZCZ&Y9WEY.X/X'X/X'X/X'X/X MYNY BX8Y N" -"WFW X NW $w DX $VBV#XFS :WFXEV H] ;W #W 9XITIX" -" +V JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X Je M~X Me 9Y >U?gMUCV7WBW IX>\\ NX *X*X,X -X 0X *X/X'X -X -XNZ 9X -XHVBVHX0XGXAX)X%X.X9Y!X%" -"X.X:Y La 'X _ @W6W MW'W.YGWFX NW=W JWCWCW NX:X NYW2W FWBW 8Z @X 0X%X0X@X@X,X0X(X@X@X" -"/X%X)Y HV IY ?V @Y3Y ?V CV /YES 6X 1\\H[ JcJc LV 0WI\\ =TFV)S;WAX 5WEW MTAVAWCS 3W 4~W.W KV ?W6W LgGU KU 4WAW @WEW 6U9V GU9V ?VBV BX 2" -"WBW AWBW AWBW AWBW AWBW AWBW AXAX X *X -X -X -X -X -X -X -X ,X*X-XGXAX)X%X1X%X1X%X1X%X1X%X!ZEZ%X9WCX.X/X'X/X'X/X'X/X LXNX AX7X NWFW !W ,W ,W ,W ,W ,W " -",]:X=Y .X9X LX9X LX9X LX9X LW ,W ,W ,W +Z=X K[x A` J~\\(y%W8W MW4W CXMW >W>W MV $x DX $VCV\"XFS 9XIXEV H_ X #Y ?g AVBX Do HXM" -"k 3Y >l HX7Z MX -X Me J~X Je =Y >V?hNUBU8XBX Ju MX *X*X,w Lq IX *~R'X -X -c 8X -XHVBVHX0XFWAX)X%X.X9Y!X%X.X;Z Ke ,X WNV MW" -"Ib +W EW;Y MW *Z;X KV:W =_ @W6W NW%W/XFWFX NW=W JWCWCW NW8X!Y:Y =W >| GW@W 8Y @X 0X%X1Y@X@Y-X0X(X@X@X/XImIX*Y GV HY @V AY1Y @V CV /XDS 6X 0YDY JdL" -"d LV 1WF[ >SFV'SW6W LgGU KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX CXBX CXBX BXAw?X *w Lw Lw Lw " -"LX -X -X -X ,X*X-XFWAX)X%X1X%X1X%X1X%X1X%X ZGZ$X:WBX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8W=X -W7W LW7W LW7W LW7W LW ,W ,W ,W ,Y:X LZ;X M" -"Y:Y MY:Y MY:Y MY:Y MY:Y \"Y=\\ LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NVV&V 4W:X %~X2TNVW \"W ;WFTFW -V JV 3X4X >X #Y ?f AWBX Dp IXNm 4X ` @W6W NW%W/WEWEW NW=W JW" -"CWCW X8X!X8X =W >| GW@W 7Y AX 0X%X1X?X?X-X0X(X@X@X/XImIX+Y FV GY AV BY/Y AV DX 1XCS 6X 0W@X KdLd LV 1VCZ ?SFV'S;WE[ 7XFX G~X .S@VBWAS @~W0W " -".P>W >W6W KfGU KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@W CW@W CW@W CW@W CW@W CXBX CX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XFXBX)X%X1X%X1X%X1X%X1X%X N" -"ZIZ#X:VAX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8X?X -X7X NX7X NX7X NX7X MW ,W ,W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X \"X=] LW6W MW6W MW6" -"W MW6W MW:W IZ9X NW:W NVLuKU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X %~X2RLW>T >{!z'~Z)}(W6W NW4W DXLX ?X@X MV KX ,X %VBV!YHS 8eEV" -" Ic ?W !W ;UETEU ,V KW 3X4X >X $Y >c ?WAX DWD^ JbG] 5X 9d DY9[ MX -X #d D~X Dd DY a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X =W >| HX@X 7Y BX 0X%X1X?X?X-X0" -"X(X@X@X/XImIX,Y EV FY BV CY-Y BV DX 1XCS 6X 1W>W KeNe LV 1VB[ ASFV'S;YI] 9YGY F~X .S@VDX@S @~W1V ,TEZ >W6W JeGU IX +U 2YIY T ?|\"}(~X)~(W6W NW4W DXKW >W@X MV KX ,X %VBV!ZIS 7cEV IYNZ8W 0W !W :RCTCR +V KW 3X4X >X %Y" -" =b >V@X DS=\\ K`C[ 6Y 8b BX9[ Nd A~X Ad HY W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MXXMW AW6W NW%W0XEWDW W=W JWCWCW!X6X#X6X >W >| HW>W 6Y CX 0X%X1X?X?X-X0X'XAXAX.XImIX-Y DV EY CV DY+Y CV DX 2X" -"BS 6X 1Vh =W6W JeGU IX 4g :g :YFX DgEV:XhCV:X/X 3X?W EX?W EX?W EX?W EX?W EX@X EX?w?" -"X *w Lw Lw Lw LX -X -X -X 5p9X-XEXCX)X%X1X%X1X%X1X%X1X%X LZMZ!XX7X NWFY !V +V +V +V +V +V +Y6W@X ,W5W NW5W NW5W NW5W MW ,W ,W" -" ,W -X7X MX8X X6X X6X X6X X6X X6X $X=_ MW6W MW6W MW6W MW6W LWS >}%~R)~V(~P)W6W NW4W" -" DWJX ?XAW L~^ $X ,X %VCV N\\LS 6aDVAW0XLZ9W 0W !W :PATAP +V KV 2X4X >X &Z =e BW@X DP8[ L^?Z 7X :h EY;\\ \"d >~X ?e LY ;U@W>Y" -"AU:W>W Ks KX *X*X,w Lq IX6f+~R'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X X IXDVCVDX)[ 4\\ -Z @W *V #W $W JX5W\"X -W5X W4W KW 0W5X MX7W" -" MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LWX >XMX BW6W W#W1WD" -"WDW W=W JWCWCW!W4W#X6X >W >| HW>W 7Y BX 0X%X1X?X?X-X0X'XAXAX.XImIX.Y CV DY DV EY)Y DV DX 2XBS 6X 2WY BSFV'S9bMV ;XFY D~X .S@h>S " -"@~W2i >g W EW>W EW>W EW>W EW>W EW>W EX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDWCX)X%X1X%X1X%X1X%X1X%X " -"Ke X=W?X.X/X'X/X'X/X'X/X I\\ >X7X NWEY \"W ,W ,W ,W ,W ,W ,X5W@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W $W=VMW MW6W MW6W MW6W MW6W " -"LW=X HX5W NW=X MVLuKU/VLuKU/V?[>U/V=Y>U/V=X=U&V 3X=W 7X FW@T ?~&~T*~V)~R*W5V NW4W EXJX ?XBX L~^ $X ,X &VBV Mb 4]CVC]4XJZ:W" -" 0W !W +T KV KV 2X4X >X 'Z X Lu MX *X*X,w Lq IX6f+~R'X -X -c 8X -XFVFVFX0XDXDX)X%X.u MX%X.r" -" ?l :X X IXDVCVDX)\\ 4Z ,Y ?W *V #W $W JX5W\"W ,W5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LWW 6Y 0X 9V LX 5`3R 0T?[?T/W:[ KWId DbKW HW5X NW +X7W JV>W =WLX BW6W W#W1WDWDW W=W JWCWCW!W4W#W4W >W >| IX>X 9Y AX 0X%X1X?X?X-X0X'XAXAX.XImIX/Y B" -"V CY EV FY'Y EV DX 2WAS ?r CV:V =^ =V 2V=Y CSFV'S8`LV e :W6W GbGU IX 4g 8c 5XFX FgFV:YX GX>X GX>" -"X GX>X GX>X GX>X FX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDXDX)X%X1X%X1X%X1X%X1X%X Jc NX>W>X.X/X'X/X'X/X'X/X HZ =X7X NWEZ #W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3" -"W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W MW>W LVLuKU/VLuKU/V>Z>U/V>Y=U/V=X=U&V 2W>X 8Y FW@T " -" ?~P(~V*~T(~Q)V4V NW4W EXJX >WBX L~^ $X ,X &VBV Ld 4WAVD`6XHZ;W 0W !W +T KV LW 2X4X >X 'Y ;i GV>X *Z M\\;Y 9X =p HZ?^ 'd " -" Id$Y 9UAWX GWEVJVEW#a >W>W 7Y 1Y 8V KY 9e8T 0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6" -"W W#W2XDWDX!W=W JWCWCW!W4W#W4W >W >| IWX GX>w?X *w Lw Lw Lw LX -X -X -X 5p9X-XCWDX)X%X1X%X1X%", -// Start of second string. -"X1X%X1X%X Ia MX?W=X.X/X'X/X'X/X'X/X GX W GX5W MW>W LVLuKU/VLuKU/V?\\?U/V?YX 8X DWBT ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^ $X ,X &VBV Kg \"" -"VEc8WFZ=W /W !W +T 4~W 5V 1X4X >X (Y -] IW>X )Y M[9X 9X >\\F\\ H[C` 'a Ca$Y 9UAV:WAU;WW )V $W 6i JX5X$X -X5X V2W LW 1W3W MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>" -"W FWEVJVEW#a >W?X 8Z 4\\ 8V K[ =iW2W IWX X *X -X -X -X -X -X -X -X ,X*X-XCXEX)X%X1X%X1X%X1X%X1X%X H_ LX@Wi >i >i >i" -" >i >i3WBX ,V2W!V2W!V2W!V2W NW ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X LVLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&" -"V 2X?W 8X CWBT ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^ $X ,X 'WCV Ii &VEe:XEZ>W /W !W +T 4~W 5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y " -"?Z@Z I]Gb '^ =^$X 9U@V:WAUXIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W2W JW;X ~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^ $X " -" ,X 'VBV Gi (VFg;WCZ?W /W !W +T 4~W 6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX '[ 8\\%Y 9UAW:WAUX XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'" -"X0X?X?X-X0X%XCXCX,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V V&V 1XAW 9" -"X @WDT ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^ *r ?V &VBV Eh *VEXIXX +Y $Z NWXHX DW6W!WW2W KX:X ?Y =X /X'X0Y@X@Y-X0X%YDXDY,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V ;X DW;V DSFV'S >XFX " -" ;V .S@VFW=S (V \"W6W :UGU IX 0XFX -V;TLU MV0U!V;TLU6Y 0X:X KX:X KX:X KX:X KX:X KX:X JWV&V 1XBX :X ?WDT ?~S,~[({ x&W4W W4W FWFX ?XFX JV \"q >V &VBV Af -VEXGX=W@ZB" -"W .W !W +T 4~W 5f 8V 0X4X >X ,Y \"Y W;X 'X NZ7X X -XDVJVDX0XAXGX)X%X.i" -" AX%X.X>Z ,\\ ?X XGW DW6W!WW2W KW9X ?Y =X /X'X/X@X@X,X0X$YEXEY" -"+X%X2~a GV H~a HV I~b HV DX 3W@S 6X 3V8V ;X DXWEW :V .TAVEW?T (V \"W6W :UGU IX /WEW .V;TKU NV/U\"V;TKU7Y /X:X KX:X KX:" -"X KX:X KX:X KX:X KXWDS >~T-~\\(y" -" Mw&W4W W4W GXFX ?XFX JV #r >V 'WCV X -Y Y!W;X 'Y Y5X =X @Y8Y HgKX 'a Ca%X 8UAV8" -"VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WGX)X&Y.X 0X&Y.X=Y *[ @X XFX EW6W!WW2W KW8W @Y ] Jt It It It It It I~iBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W L" -"WCX IV=V=V.V$V.VFYKZFV.VFY7V.V$V&V 0XCW ;Y =WFT >~T-~\\'w Ku%W4W W4W GXEW >WFW IV #q =V 6~X JSN^ /VEWCW?W=ZDW .W !W :~W" -" 5f 9V /X4X >X .Y MX\"W:X &X Y5X >Y @X6X FcJX &d Id%X 8UAV8VAU>X8X X4X$X +X+X+X -X /X)X+X/X'X -X -XH[ @X -XCVLVCX0X@XHX(X'X-X /X'X-XXFX EW6W!WV-V%V-VGYIZHV-VGY7V-V%V%V /WDX ;X ~T-~\\'v Is" -"$W4W W4W GWDX ?XGW HV %r =V 6~X JSJ[ 0VEVAV?WX ?X6X D`IX $d Ne#X 8UAV8" -"VBU=x X4X$X +X+X+X -X /X)X+X/X'X -X -XG[ AX -XCVLVCX0X?WHX(X'X-X /X'X-X;Y *Y @X WDW EW6W!WV>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW N~X'VGT =~T-~\\&u Ir#W4W NV4W HXDX ?XHX HV KX ,V 6~X JSHZ 2VDVAV?W;ZGW -W !W \"V " -"Lf :W .X6X =X 0Z LY#~ /X NX5X >X @X5Y AYFX !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X)X+X/X'X -X -XF[ BX -XCWNWCX0X?XIX(X'X-X /X'X-X:X )Y AX XDX FW6W!WV?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX N~X'WHT =~T-~\\%s Gp\"W4W NV4V GXCW >WH" -"X HW LX ,V 6~X JSGY 3VDWAW@W:ZIW ,W !W \"V Lf :W .X6X =X 1Z JX#~ /X NX5X ?Y @X4X .X Md A~X Ad LX 8UAV8VBU>z!X3X%X +X+X+" -"X -X /X)X+X/X'X -X -XE[ CX -XBVNVBX0X>WIX(X'X-X /X'X-X9X *Y AX Q.X $T>Z?T0W8W HW5W\"WWCX FW6W!WXFX >V ,SBVBWCS &V \"W6W :U" -"GU *m 8XFX .VWIX(X'X/X'X/X'X/X'X/X'X KZMZ XHW6X-X/X'X/X'X/X'X/X GX XIW GW LX ;~X JSFX 3VDV?V@W9ZJW +V \"W !V V -X6X =X 2Z IX#" -"~ /X NX5X ?X ?X4X .X Jd D~X Dd IX 8UAV8VCV>z!X3X%Y ,X,Y+X -X /Y*X+X/X'X -X -XD[ DX -XBVNVBX0X>XJX(Y)X,X /Y)X,X9Y *X AX XBW FW6W!WXJX" -"(Y)X.Y)X.Y)X.Y)X.Y)X KZKZ!YJW6X,X/X'X/X'X/X'X/X GX |\"X3X$X ,X,X*X -X .X*X+X/X'X -X -XC[ EX" -" -XA\\AX0X=WJX'X)X,X .X)X,X8X *X AX XBX GW6W!WW 9X =\\KW >SEWWJX FW LX <~X JSEX 6WCV?V@W7ZMW *W #W !V !W -X6X =X 4Z GX#~ /X NX5X @X >X4X " -"/X De J~X Je DX 8U@V:WDV>|\"X3X$X ,X-Y*X -X .X*X+X/X'X -X -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX W WJW DW MX .VCV" -" :SDW 6VBV?V@W6b )W #W !V !V +X8X X4X /X Ad L~X Ld AX 8VAV:WDU=|\"X3X$Y -X-Y*X -X .Y+X+X/X'X -X -XA[ GX -XA\\AX0XWKVDVKW\"XLX 9WJW =Z #X :V MX AUEVKVDU/X:Y IW5W#WX@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4W >W X4X 0X =d ~X" -" d LUAWX2X#X3X#X -X.Y)X -X -X+X+X/X'X -X -X@[ HX -X@Z@X0XW ,W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V MW @VHXJWHV-W:Y IW5W#WY>W1WDWDW W=W JWCWCW\"X4W#W4W >W W MW7X MW7X " -"MW7X MW7X EWJW AX5W GWJW AXCVCW%X0W%X0W%X0W%X0W\"V +WJX ?X 2WLT 9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW MX 0VBV :SDW " -"7VAV?V@X5_ (W #W !V \"W +X8X XLV;VLX1Y?Y >X 9Z 2W %W )W EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4W MX5W\"W5X MW AW FW ,W7X FXJX" -" =WMVBVMW#YJY ;WKX >Y W :V MW ?dId,W;Z IW5W#W=W DW4W!W )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L" -"Y-Y%Y IV JY LYKVKY MY5Y MYJVJY $X 2XBS 6X 2q 9X :V #\\ 7TDgFT /XFX EV )TFV>VJT #V \"W6W :UGU +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W" -"$X1W$X1W$X2X%X7X LY .X -X -X -X -X -X -X -X ,X.Y*X;XMX&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y ZAZ$_3Y*X1X%X1X%X1X%X1X FX W3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Z NX -X " -"-X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W MW7X MW7X MW7X MW7X EWKX AX5W GWKX @XDVDX$X2X$X2X$X2X$X2X\"V +XKW ?X 1WMT 7`JQKa" -"'fLQLf Kg W >WLW BX NY 1VBV :SDW 8V@V?V?W4] &V $W V \"V *Y:Y YGW>X0X$X4Y\"Y /X/Y(X -X ,Y-X+X/X'X -X -X>[ JX -X@Z@X0X;XMX%Y/Y*X ,Y/Y*X6Y -X AX ;Y3Y IXLX =WLV;VLW0X=Y ?X :Z 1W $V )W EW8Y JY7X\"X -X7Y X " -")W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW AW FW ,X8X EWJW Y NW :V MW >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7" -"X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y JV KY JYLVLY KY7Y KYKVKY #X 2XBS 6X 3t ;X :V ![ 8TCfFT .XFX FV )U" -"GV>WKT MW7X :UGU ,XFX *V=TBU&V2W!V=TBU=X -X0X&X0X&X0X&X0X&X0X&X0W%X7X KY /X -X -X -X -X -X -X -X ,X/Y)X;XMX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y Z?Z$" -"^4Y)Y3Y%Y3Y%Y3Y%Y3Y FX XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX @X /WNT 7`JQJ_&eKQKe Je :d KW4W MW8W HW>X ?XNX AX Y 1VCV 9SDW 9V?V?V?X4\\ " -" &W %W V \"V )X:X ;X 9Z CX 4X (Y KW7X AX W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X JV " -"KX HYMVMY IX7X IYLVLY \"X 1XCS 6X 4v X ?XNX AY Y4P VBV 9SDW 9V?V?V?Y4Z %W %W V #W )X:X ;X :Z CY 4X (Y KX9Y AX ;X6X 1Y 1e /e @U@XB[JXW BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW " -"NW=W IWDWEX!Z8X!X8X =W :W:W LX0X Y 2X (Y7Y Nv#X0X ?X AY1Y V IV JV FYNVNY GV5V GYMVMY !X 1XCS 6X 5x =X :V MZ 8T?ZBT *VDV FV 'T&T KX" -"8X :UGU ,VDV )VWNX @Y !Z6Q VBV KP>SEW 9V>WAW>X3Z &W %W V " -" #V 'XU?ZH^MZ\\ JX8X\"W?W AX9Y X *W6W CVNX ?X;X JX9Y NW@[@W/XFWFX NW=W IXEWEX!Z8X!X8W ;W ;W;X MX.X\"Y 1X 'Y9Y Lt\"X0X ?X @Y3Y MT HV IT Dj ET3T EYNVN" -"Y X 0XDS 6X 6ZM`LY >X :V LY 7T)T (UCU ET(T JX9Y :UGU ,UCU )V;m.V3V NV;mCY7P HX.X(X.X(X.X(X.X(X.X(X.X(X6X IY.R&X -X -X -X -" -"X -X -X -X ,X2Z'X9a$Z3Y&Z3Y&Z3Y&Z3Y&Z3Y!Z9Z&Z3Y&Y5Y#Y5Y#Y5Y#Y5Y EX `" -" >Y !Y8S MX +VBV KQ?SFX 9V=VAV=Y6] &V &W NV BX 1X 1V 'Y>Y :X X:W JY;Z NXB]BX.XGWGX MW=W H" -"XFWFX [:X NX:X ;W :WX HXX 9X =Z 1P2Z 3X GQ5Z GX=Y @X 9Y:Y KP8Z GX -X 4^ 1^ +X 5U?gM_9W,W%X7Z L[4U&X6]%X -X )[2X+X/X'X -X -X9[ X -X&X0X8`\"Z7Z'X )Z7Z'X3X%T2Y ?X 9Z9Z E` :" -"_9_3Y7Y BX >Z -W #W +W DX=\\ J\\=Y LY7P HY=\\ LY5R JW -Y?] MW6W MW ,W ,W@Y EW ,W7W7W=W6W MYX LX.X#Y 0X %Y=Z Gl MX0X ?X ?Z7Z JP FV GP @f AP/P Ah MX " -"/YFSDP BX 8ZFVEY @X :V JX 7V.U %SAS CU.U HZ\\=Y B^ 7r Gr Gr Gr Gr KV (_ BX )Y S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[ " -" $[=U MX ,VBV JUCSHY :V;WCW<[Z 0R5Z 2X GT9[ GY?Z AY 9[>[ KR;Z FX -X 1[ 1[ (X 5V>dL^9X,X&X9[ J[7W&X9_$X " -"-X (\\6Z+X/X'X -X -X8[!X -X&X0X8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W DY?] J]?Y KZ:R GY?] LZ8T JW -ZA^ MW6W MW ,W ,W?Y FW ,W7W7" -"W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY IW :V MW HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY" -">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[ ?V 6d >f LX /[HSFR BX 9Z3Y AX :V IX 7V1V #R@R BU0U G[>[ :UGU ,R@R 'V(U)V6W" -" LV(UU IX,X*X,X*X,X*X,X*X,X*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ;Z CX Y JW6W LY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y HY@] KY@] KY@] KY@] B^ >]?Y A^ 6o Do Do Do " -"Do IV (_ CX (Y S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\ %[@W MX ,VBV JXFSIZ :V:WEW:\\@e (V 'V MV BX 1X 2V $ZDZ 8X ?Z /U;] 2X GV=" -"\\ EZC[ @X 7[@[ JT?[ EX -X /Y 1Y &X 5V=bK\\7X,X&X<^ I]=Z&X=b#X -X ']:\\+X/X'X -X -X7[\"X -X&X0X7_ \\?\\%X '\\?\\%X2X&Z<\\ >X 7[?[ B^ 9^7^4Y5Y CX ?Y +W \"V +W " -" DZB_ J_CZ I[>T G[C_ K[=W JW ,\\GXNW MW6W MW ,W ,W>Y GW ,W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY HW :V MW GZFYNY N]AZ N" -"WCX _ FX0X ?X =\\?\\ >V 5b W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ " -"GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\ 3l Al Al Al Al HV (^ BX (X NS (S ,Z .Y FW4W In GX:X ?^ 9_ (]FZ MX " -",VBV J[ISL\\ :V9XGX9^Fi )W )W MV BX 1X 3W #[H[ Et Mx MZC_ 1X GZD^ C[G\\ @Y 7^F] IXF] DX -X ,V 1V #X 4V<^IY5X*X'y G_D^&{!y NX &`B`+X/X'X -X -X6[#" -"w LX&X0X7_ N^E^$X &^E^$X2Y'^C^ =X 7^E^ B^ 8]7]4Y3Y DX @~U&W \"W ,W C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX" -"G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'XP @W8W 3~W :_GaKP @UGU ,P>P 'V&U+V6V KV&" -"U;]GZ JX*X,X*X,X*X,X*X,X*X,Y,Y,X4y7_D^&y Ny Ny Ny NX -X -X -X ,{\"X7_ N^E^ L^E^ L^E^ L^E^ L^E^ MV/V(dE^ N^E^ L^E^ L^E^ L^E^ BX \\ Av 6W :V MW FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#" -"hKh)s JW<] Lu LWNm Hp 6` Bl K~W'x MX 1iEi HX CX0X ?X ;u X :V HW 3X=X )X\\ /c 8c 8c 8c 8c CV '\\ ?T %W U *T *W ,V DW4W Gj EW8W " -">\\ 5~P In LX -VBV Is 9V7g6qJZ *V )V LV BX 1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X &P 1P LX 3V 3X*X'w Cv%x My NX #x(X/X'X" -" -X -X4[%w LX&X0X5] Ls\"X $s\"X1Y(w ;X 5s ?\\ 7\\5\\5Y1Y EX @~U&W !V ,W BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p" -" ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V MW EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<] Ks KWMk Fn 5` Aj J~W'x MX 1iEi HX CX0X ?X :s ;V 2\\ 6" -"^ HX +n Lz MR,R =X :V HW 1ZEZ %ZDZ 0~W :WNfM\\ @UGU !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-X3y5v%y Ny Ny Ny NX -X -X -X ," -"x NX5] Ls Hs Hs Hs Hs IR+R(WMs Js Hs Hs Hs @X R $V NU *U *U *U DW4W Fh DW8X ?\\ 4~ Hl KX -VBV Hp 8V5e4nGZ +W +W LV BX" -" 1X 3V j Ct Mx Mr -X Gq =j >Y 3p Gl AX -X 2X 3W 5X(X(u ?s$v Ky NX \"v'X/X'X -X -X3[&w LX&X0X5] Kq!X #p X0X(v :X 4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W " -" AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5ZZ @v 6W :V MW DgI\\$s He 5l Dn EW6W @Y " -">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q :V 1Z 4\\ GX *m Lz LP*P X X ?v 6W :V MW CeG[$r Fc 2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m 8V 0" -"X 2Z FX (j Kz AX :V HW -g Lh ,~W :WMdL\\ @UGU \"V$U-V5i0V$U7i HX(X.X(X.X(X.X(X.X(X.X(X/X2y1o\"y Ny Ny Ny NX -X -X -X ,t" -" JX4\\ Im Bm Bm Bm Bm %VHm Dm Bm Bm Bm =X eJW GeJW" -" GeJW GeJW ?X ;WJe 9X MW &Z =U W ,W *R &Q BW4W B` AW6W >[ /y Dd GX -VCV Af 5V2a.gBZ ,W -W KV CX 0X 4V " -" Kd @t Mx Km *X Ek 6d ;X .h Bh >X .X 1X 1W 7X(X(q 7j Np Ey NX Mm\"X/X'X -X -X1[(w LX&X0X4\\ Gi LX Ni LX/X$n 7X 0i 9Z 5[5[6Y-Y GX @~U&W V -W " -" >cIW JWIb k EW6W @Y ?" -"W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0X ?X 5i 6V /V 0X EX &f Iz AX :V /P;W *c Gb )~W :WK`I[ @UGU " -" #V#U.V4i1V#U6f FX(X.X(X.X(X.X(X.X(X.X(X/X2y/j Ny Ny Ny Ny NX -X -X -X ,p FX4\\ Gi >i >i >i >i $VEi @i >i >i >i ;X i0g ;i >i >i >i HW ,W ,W ,W #d BW6W Ef ;f ;f ;f ;f JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X MW %Y =T X -X )P %P AW4W ?Z" -" >W6X ?Z ,w B` EX .VBV <] 1V0]*b?[ -W -W KV CW /X 4V I` >t Mx Hg 'X Bf 2` :X +d =b ;X .W 0X 1X 9X&X)m 0d Kj ?y NX Jg " -"NX/X'X -X -X0[)w LX&X0X3[ Dc IX Kf LX/Y!g 4X .e 7Z 5Z3Z7Y+Y HX @~U&W V -W =`GW JWG^ 7b 9^GW Ad CW \"YDW MW6W MW ,W ,W7Y NW ,W7W7W=W6W B` @WG^ 9" -"^GW MW (c 2] 3_GW @Z 3X:X*Y4Y @X ?v 6W :V MW ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ F~[)x MX 1iEi HX CX0X ?X 2c " -"3V .T .V DX $b Gz AX :V /R>X &[ ?Z %~W :WJ^GY ?UGU #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X " -"-X ,j @X3[ Dc 8c 8c 8c 8c !VBc ;e :e :e :e 9X Y BS .V,W#Z ;V -V 7W ;W EX ;\\ 6] " -"+Z 5\\ 5Z WGXBU FX=X E` \"W >] @WDY 3Z 2X C[ >T :[ KV /TAY " -" EWGXBU =UGU BT 6V +V +V ,Y ?\\ +[ 0[ 0[ 0[ 0[ KT=[ 2[ 0[ 0[ 0[ 7Z ;Y .Y .Y .Y .Y .Y -" -"Y2\\\"Z /\\ 1\\ 1\\ 1\\ CZ 3Z /Z /Z /Z /Z FVCZ 1Y .Y .Y .Y ,W :WDX 2W LW 7R #S" -" >W /W 8W :V \"W 5X )X &Z CW NV .W :W %W" -" @W :W -X -W :V MW LW FW ?W >W NW 0W =W 3S GV /XGZ " -" DW HUGU AT %T 'R JT " -" #T (X :W NX LW 7S =V /V 7W :V \"W" -" 4X'Q &Y %Z DW NV .W :W %W @W :W -W ,W :V MW " -" LW FW ?W >W NW 0W =W 3S GV /j CW HUGU @T " -" %T 'P HT \"Q 'W 9W NW KW " -" 7S =W 1W 7V :W \"V 2X)R &X #Z " -" EW NW /W :W %W @W :W -W ,X ;V NX LW FW ?W >W NW 0W =W " -" 3S GV /j CW HUGU @U &U " -" U \"P 'W 9W NW KV 6S " -" W NW 0W =W 3S GV /h " -" AW HUGU ?T %T NT " -" )X 9W X KV 6S W NW 0W =W 3S GV .f @W HUGU ?U &" -"U U *W 8W W JV " -" 6S ;V 3V 6V :W \"V .[5[ *Y Z " -" Ha (W :a W NW 0W =W " -" 3S GV +a >W HUGU >T %T " -" NT +X 8W !X (VIV 6S :V 5V 5U" -" 9W \"U +\\;] )X MZ Ia (W :a =Y %W ?W :W " -" /W )[ ?V #[ KW FW ?W >W NW 0W =W 3S GV 'Z ;W " -" HUGU >U &U U ,W 7W !" -"W 'VIV 6S :V 6W 6V 4V *_C` " -" )Y LZ Ja :a (P7Y $W ?W :W 0X (b GV +b JW FW ?W >W " -" NW 0W =W 3S GV 7W HUGU >U &U " -" U -X 7W \"X 'VJW " -" 6S 9V 7V 5U 3U 'x (Z KZ Ka :a " -" (R:Z $W ?W :W 0X (b GV +b JW FW ?W >W NW 0W =W 3S " -" GV 7W #U &U U " -" -X 7W \"X &UJW 6S 9W 9W " -" Bu ([ IZ La :a (T>[ $X ?W :W 1X &a GV +a " -" IW FW ?W >W NW 0W =W 3S GV 7W $V " -" 'V !V .X 6W #X %VLW " -" 5S 2p -a " -" 8XE] %Y >W :W 3Z $_ GV +_ GW FW ?W >W NW 0W =W " -" 3S GV 7W /QGW 2QGW ,QG" -"W 0Z 6W %Z %a 5S " -" 0l +a 8p +_ >W :W ;a !] G" -"V +] EW FW ?W >W NW 0W =W 3S GV 7W /` " -" 1` +` 7a 5W -a #` " -" >e '` " -" 7o *^ =W :W ;` KY GV +Y AW FW ?W >W NW 0W =W " -" 3S GV 7W /` 1` +` " -" 7` 4W -` \"_ " -" 8\\ #_ \"} 3n )^ =W :W ;` 9V " -" BW FW ?W >W NW 0W =W 'V 7W /_ " -" 0_ *_ 6` 4W -` !] " -" -] " -" } 3l '] W NW 0W =W " -" 'V 7W /^ /^ )^ " -" 5_ 3W -_ N[ " -" ,[ M} 2j &\\ ;W :W ;^ 7V BW " -" FW ?W >W NW 0W =W 7W -Y *Y " -" $Y 2^ 2W -^ LX " -" *X J} " -" /d #Z 9W :W ;\\ 5V BW FW ?W >W NW 0W =W " -" 7W " -" /\\ 0W HT " -" I} *[ NW 6W :W ;Z 3V BW FW ?W >W" -" NW 0W =W 7W " -" /Z .W " -" =} " -" " -" D" }; - - // 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) { - cimg::mutex(1); - static std::FILE *res = stderr; - if (file) res = file; - cimg::mutex(1,0); - return res; - } - - // Return number of available CPU cores. - inline unsigned int nb_cpus() { - unsigned int res = 1; -#if cimg_OS==2 - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - res = (unsigned int)sysinfo.dwNumberOfProcessors; -#else - res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN); -#endif - return res?res:1U; - } - - // Lock/unlock mutex for CImg multi-thread programming. - inline int mutex(const unsigned int n, const int lock_mode) { - switch (lock_mode) { - case 0 : cimg::Mutex_attr().unlock(n); return 0; - case 1 : cimg::Mutex_attr().lock(n); return 0; - default : return cimg::Mutex_attr().trylock(n); - } - } - - //! 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); - - //! 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) { - cimg::mutex(3); - static unsigned long timer = 0; - if (!timer) timer = cimg::time(); - cimg::mutex(3,0); - 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 - -#include - - // Use a custom RNG. - inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) { - static unsigned long next = 1; - cimg::mutex(4); - if (set_seed) next = (unsigned long)seed; - next = next*1103515245 + 12345 + rand(); - cimg::mutex(4,0); - 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); } - } - - inline bool _is_self_expr(const char *expression) { - if (!expression || *expression=='>' || *expression=='<') return false; - for (const char *s = expression; *s; ++s) - if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) return true; - return false; - } - - //! 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; - 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 }; - cimg::mutex(5); - 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); } - cimg::mutex(5,0); - return res; - } - - // Return string that identifies the running OS. - inline const char *stros() { -#if defined(linux) || defined(__linux) || defined(__linux__) - const char *const str = "Linux"; -#elif defined(sun) || defined(__sun) - const char *const str = "Sun OS"; -#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) - const char *const str = "BSD"; -#elif defined(sgi) || defined(__sgi) - const char *const str = "Irix"; -#elif defined(__MACOSX__) || defined(__APPLE__) - const char *const str = "Mac OS"; -#elif defined(unix) || defined(__unix) || defined(__unix__) - const char *const str = "Generic Unix"; -#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - const char *const str = "Windows"; -#else - const char - *const _str1 = std::getenv("OSTYPE"), - *const _str2 = _str1?_str1:std::getenv("OS"), - *const str = _str2?_str2:"Unknown OS"; -#endif - return str; - } - - //! 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() { - cimg::mutex(6); - static char randomid[9] = { 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)))); - } - cimg::mutex(6,0); - 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(s_path,1024,"%s",p); \ - cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",s_path,cimg_file_separator,filetmp); \ - if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ - } - static char *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_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) { - *s_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) { - cimg::mutex(7,0); - throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); - } - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[MAX_PATH]; - std::memset(s_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,s_path,0x0026,false)) { - const char *const pfPath = std::getenv("PROGRAMFILES"); - if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH-1); - else std::strcpy(s_path,"C:\\PROGRA~1"); - } -#else - std::strcpy(s_path,"C:\\PROGRA~1"); -#endif - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_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(s_path,".\\convert.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"convert.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./convert"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"convert"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_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(s_path,".\\gm.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gm.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gm"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gm"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_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(s_path,".\\medcon.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(s_path,sizeof(s_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"medcon.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./medcon"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"medcon"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\ffmpeg.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"ffmpeg.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./ffmpeg"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"ffmpeg"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\gzip.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gzip.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gzip"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gzip"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\gunzip.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gunzip.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gunzip"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gunzip"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\dcraw.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"dcraw.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./dcraw"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"dcraw"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\wget.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"wget.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./wget"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"wget"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 *s_path = 0; - cimg::mutex(7); - if (reinit_path) { delete[] s_path; s_path = 0; } - if (user_path) { - if (!s_path) s_path = new char[1024]; - std::memset(s_path,0,1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path = new char[1024]; - std::memset(s_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\curl.exe"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"curl.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./curl"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"curl"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_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 digits, 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 (*ext) cimg_snprintf(format,sizeof(format),"%%s_%%.%ud.%%s",digits); - else cimg_snprintf(format,sizeof(format),"%%s_%%.%ud",digits); - std::sprintf(str,format,body,number,ext); - 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; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - 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; - if (buttoncode) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - 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; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - 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; - if (amplitude) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - 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; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - 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; - if (keycode) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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 = false; - 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() { - if (!cimg::X11_attr().display) return; - if (cimg::mutex(13,2)) { cimg::sleep(10); return; } - pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); - cimg::mutex(13,0); - } - - 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; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - } 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; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - if (nx!=_window_x || ny!=_window_y) { - _window_x = nx; _window_y = ny; _is_moved = _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - } 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; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } 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; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } break; - } - } - - static void* _events_thread(void *) { // Thread to manage 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. - 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) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); - 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) { - cimg::mutex(14); - - // 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) { - 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().events_thread = new pthread_t; - pthread_create(cimg::X11_attr().events_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); - cimg::mutex(14,0); - } - - 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(); - - XUnlockDisplay(dpy); - 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-1)/2,(img._height-1)/2,(img._depth-1)/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-1)/2,(img._height-1)/2,(img._depth-1)/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; - show(); - Display *const dpy = cimg::X11_attr().display; - 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-1)/2,(img._height-1)/2,(img._depth-1)/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; - _mutex = CreateMutex(0,FALSE,0); - _is_created = CreateEvent(0,FALSE,FALSE,0); - _thread = CreateThread(0,0,_events_thread,arg,0,0); - 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-1)/2,(img._height-1)/2,(img._depth-1)/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-1)/2,(img._height-1)/2,(img._depth-1)/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-1)/2,(img._height-1)/2,(img._depth-1)/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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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. - The cubic interpolation uses Hermite splines. - \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. - The cubic interpolation uses Hermite splines. - \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 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 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 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 (%u,%u) defines no vertices but %u primitives, %u colors and %lu opacities", - _width,primitives._width,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 vertex 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 (!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 (%u,%u) defines an invalid primitive [%u] of size %u", - _width,primitives._width,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 (%u,%u) defines no color for primitive [%u]", - _width,primitives._width,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 (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", - _width,primitives._width,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 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 full_check 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 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 number of vertices / primitives. - if (!full_check) { - const unsigned long minimal_size = 8UL + 3*nb_points + 6*nb_primitives; - if (_data + minimal_size>ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", - nb_points,nb_primitives,size(),minimal_size); - return false; - } - } - - // Check consistency of vertex data. - if (!nb_points) { - if (nb_primitives) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) defines no vertices but %u primitives", - nb_points,nb_primitives,nb_primitives); - return false; - } - if (ptrs!=ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) is an empty object but contains %u value%s more than expected", - nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":""); - return false; - } - return true; - } - if (ptrs+3*nb_points>ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) defines only %u vertices data", - nb_points,nb_primitives,(unsigned int)(ptre-ptrs)/3); - return false; - } - ptrs+=3*nb_points; - - // Check consistency of primitive data. - if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) defines %u vertices but no primitive", - nb_points,nb_primitives,nb_points); - return false; - } - - if (!full_check) return true; - - 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) defines an 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], %u values missing", - nb_points,nb_primitives,p,(unsigned int)(ptrs-ptre)); - return false; - } - } - - // Check consistency of color data. - if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) defines 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], %u values missing", - nb_points,nb_primitives,c,(unsigned int)(ptrs-ptre)); - return false; - } - } - - // Check consistency of opacity data. - if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) defines 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 labelM; - CImgList code; - CImg level, opcode, labelMpos, label1pos; - 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 parentheses/brackets 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=='('||*ps=='['?lv++:*ps==')'||*ps==']'?--lv:lv); - if (lv!=0) { - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Unbalanced parentheses/brackets in specified expression '%s'.", - pixel_type(),calling_function, - expr._data); - } - // Init constant values. - mem.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; - labelMpos.assign(8); - label1pos.assign(128,1,1,1,~0U); - label1pos['w'] = 3; - label1pos['h'] = 4; - label1pos['d'] = 5; - label1pos['s'] = 6; - label1pos[0] = 7; // pi - label1pos['e'] = 8; - label1pos['x'] = 9; - label1pos['y'] = 10; - label1pos['z'] = 11; - label1pos['c'] = 12; - 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 || val==1 || val==2) _cimg_mp_return((int)val); - 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 || val==100 || val==200) _cimg_mp_return((int)(val/100)); - 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' : case 'h' : case 'd' : case 's' : - case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : _cimg_mp_return(label1pos[*ss]); - case 'u' : if (label1pos['u']!=~0U) _cimg_mp_return(label1pos['u']); _cimg_mp_opcode2(0,0,1); - case 'g' : if (label1pos['g']!=~0U) _cimg_mp_return(label1pos['g']); _cimg_mp_opcode0(1); - case 'i' : if (label1pos['i']!=~0U) _cimg_mp_return(label1pos['i']); _cimg_mp_opcode0(2); - case '?' : _cimg_mp_opcode2(0,0,1); - } - if (ss1==se1) { - if (*ss=='p' && *ss1=='i') _cimg_mp_return(label1pos[0]); // pi - if (*ss=='i') { - if (*ss1=='m') { if (label1pos[1]!=~0U) _cimg_mp_return(label1pos[1]); _cimg_mp_opcode0(57); } // im - if (*ss1=='M') { if (label1pos[2]!=~0U) _cimg_mp_return(label1pos[2]); _cimg_mp_opcode0(58); } // iM - if (*ss1=='a') { if (label1pos[3]!=~0U) _cimg_mp_return(label1pos[3]); _cimg_mp_opcode0(59); } // ia - if (*ss1=='v') { if (label1pos[4]!=~0U) _cimg_mp_return(label1pos[4]); _cimg_mp_opcode0(60); } // iv - } - if (*ss1=='m') { - if (*ss=='x') { if (label1pos[5]!=~0U) _cimg_mp_return(label1pos[5]); _cimg_mp_opcode0(61); } // xm - if (*ss=='y') { if (label1pos[6]!=~0U) _cimg_mp_return(label1pos[6]); _cimg_mp_opcode0(62); } // ym - if (*ss=='z') { if (label1pos[7]!=~0U) _cimg_mp_return(label1pos[7]); _cimg_mp_opcode0(63); } // zm - if (*ss=='c') { if (label1pos[8]!=~0U) _cimg_mp_return(label1pos[8]); _cimg_mp_opcode0(64); } // cm - } - if (*ss1=='M') { - if (*ss=='x') { if (label1pos[9]!=~0U) _cimg_mp_return(label1pos[9]); _cimg_mp_opcode0(65); } // xM - if (*ss=='y') { if (label1pos[10]!=~0U) _cimg_mp_return(label1pos[10]); _cimg_mp_opcode0(66); } // yM - if (*ss=='z') { if (label1pos[11]!=~0U) _cimg_mp_return(label1pos[11]); _cimg_mp_opcode0(67); } // zM - if (*ss=='c') { if (label1pos[12]!=~0U) _cimg_mp_return(label1pos[12]); _cimg_mp_opcode0(6); } // cM - } - } - - // 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') is_valid_name = false; - else for (const char *ns = ss+1; ns'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { - is_valid_name = false; break; - } - if (!is_valid_name) { - *se = saved_char; - 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()?"...":""); - } - const unsigned int pos = compile(s+1,se); - if (variable_name[0] && variable_name[1] && !variable_name[2]) { // Check for particular case of a reserved variable. - const char c1 = variable_name[0], c2 = variable_name[1]; - if (c1=='p' && c2=='i') variable_name.fill((char)0,(char)0); // pi - else if (c1=='i') { - if (c2=='m') variable_name.fill(1,0); // im - else if (c2=='M') variable_name.fill(2,0); // iM - else if (c2=='a') variable_name.fill(3,0); // ia - else if (c2=='v') variable_name.fill(4,0); // iv - } else if (c2=='m') { - if (c1=='x') variable_name.fill(5,0); // xm - else if (c1=='y') variable_name.fill(6,0); // ym - else if (c1=='z') variable_name.fill(7,0); // zm - else if (c1=='c') variable_name.fill(8,0); // cm - } else if (c2=='M') { - if (c1=='x') variable_name.fill(9,0); // xM - else if (c1=='y') variable_name.fill(10,0); // yM - else if (c1=='z') variable_name.fill(11,0); // zM - else if (c1=='c') variable_name.fill(12,0); // cM - } - } - if (variable_name[1]) { // Multi-char variable. - int label_pos = -1; - cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; } // Check for existing variable with same name. - if (label_pos<0) { // If new variable. - if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0); - label_pos = labelM._width; - variable_name.move_to(labelM); - } - labelMpos[label_pos] = pos; - } else label1pos[*variable_name] = pos; // Single-char variable. - _cimg_mp_return(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==']') { - const bool is_relative = *ss=='j'; - if ((*ss=='i' || is_relative) && *ss1=='[') { - if (*ss2==']') _cimg_mp_opcode0(2); - _cimg_mp_opcode1(is_relative?69:68,compile(ss2,se1)); - } - } - 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(3,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=='k'?71:ss[1]=='i'?48:ss[1]=='a'?49:70,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(5,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; - if (variable_name[1]) { cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]); } // Multi-char variable. - else if (label1pos[*variable_name]!=~0U) _cimg_mp_return(label1pos[*variable_name]); // Single-char variable. - *se = saved_char; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%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_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; - const unsigned int target = opcode(1); - mem[target] = (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; - const unsigned int target = opcode(1); - mem[target] = (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; - const unsigned int target = opcode(1); - mem[target] = (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; - const unsigned int target = opcode(1); - mem[target] = (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; - const unsigned int target = opcode(1); - mem[target] = (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 values(opcode._height-2); - double *p = values.data(); - for (unsigned int i = 2; i values(opcode._height-3); - double *p = values.data(); - for (unsigned int i = 3; i::is_nan(val); - } - double mp_isinf() { - const double val = mem[opcode(2)]; - return cimg::type::is_inf(val); - } - double mp_isint() { - const double val = mem[opcode(2)]; - return (double)(cimg::mod(val,1.0)==0); - } - double mp_isbool() { - const double val = mem[opcode(2)]; - return (val==0.0 || val==1.0); - } - double mp_rol() { - return cimg::rol(mem[opcode(2)],(unsigned int)mem[opcode(3)]); - } - double mp_ror() { - return cimg::ror(mem[opcode(2)],(unsigned int)mem[opcode(3)]); - } - double mp_lsl() { - return (long)mem[opcode(2)]<<(unsigned int)mem[opcode(3)]; - } - double mp_lsr() { - return (long)mem[opcode(2)]>>(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)]; - } - double mp_ioff() { - const unsigned long off = (unsigned long)mem[opcode(2)]; - if (off>=reference.size()) return 0; - return (double)reference[off]; - } - double mp_joff() { - const int x = (int)mem[9], y = (int)mem[10], z = (int)mem[11], c = (int)mem[12]; - const unsigned long off = reference.offset(x,y,z,c) + (unsigned long)(mem[opcode(2)]); - if (off>=reference.size()) return 0; - return (double)reference[off]; - } - - // 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_log2, // 3 - &_cimg_math_parser::mp_int, // 4 - &_cimg_math_parser::mp_arg, // 5 - &_cimg_math_parser::mp_cM, // 6 - &_cimg_math_parser::mp_jxyzc, // 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_ioff, // 68 - &_cimg_math_parser::mp_joff, // 69 - &_cimg_math_parser::mp_med, // 70 - &_cimg_math_parser::mp_kth // 71 - }; - 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. - const unsigned int target = opcode(1); - mem[target] = (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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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 = cimg::_is_self_expr(expression)?+*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,false); - 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,false); - 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. - **/ - double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const { - if (!expression) return 0; - return _cimg_math_parser(*this,expression,"eval").eval(x,y,z,c); - } - - //! Evaluate math formula on a set of variables. - /** - \param expression Math formula, as a C-string. - \param xyzc Set of values (x,y,z,c) used for the evaluation. - **/ - template - CImg eval(const char *const expression, const CImg& xyzc) const { - CImg res(1,xyzc.size()/4); - if (!expression) return res.fill(0); - _cimg_math_parser mp(*this,expression,"eval"); - const t *ps = xyzc._data; - double x, y, z, c; - cimg_for(res,pd,double) { - x = (double)*(ps++); y = (double)*(ps++); z = (double)*(ps++); c = (double)*(ps++); - *pd = mp.eval(x,y,z,c); - } - return res; - } - - //! 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 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-4f; - 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,(unsigned int)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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 = cimg::_is_self_expr(expression)?+*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,(unsigned int)(p-ptrs),1,1,1,false).move_to(list); \ - CImg(s,(unsigned int)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,(unsigned int)(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; - cimg::mutex(8); - 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; - } - } - cimg::mutex(8,0); - 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; - cimg::mutex(8); - if (!colormap) { - CImg tmp(1,256,1,3,1); - tmp.get_shared_channel(0).sequence(0,359); - colormap = tmp.HSVtoRGB(); - } - cimg::mutex(8,0); - 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; - cimg::mutex(8); - 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); - } - cimg::mutex(8,0); - 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; - cimg::mutex(8); - if (!colormap) colormap.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3); - cimg::mutex(8,0); - 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; - cimg::mutex(8); - 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); - } - cimg::mutex(8,0); - 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; - cimg::mutex(8); - 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); - } - cimg::mutex(8,0); - 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; - cimg::mutex(8); - 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); - } - cimg::mutex(8,0); - 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,(T)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,nyi,zi,2); - I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1); I(0,0,1,2) = (float)ref(xi,yi,nzi,2); - I(1,0,1,0) = (float)ref(nxi,yi,nzi,0); I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2); - I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); - I(0,1,1,0) = (float)ref(xi,nyi,nzi,0); I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,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; - if (is_empty()) return 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; - if (is_empty()) return 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 { - CImgList res; - if (is_empty()) return res; - if (!values) return CImgList(*this); - if (values.size()==1) return get_split(*values,keep_values,is_shared); - 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; - } - - //! Split the image into a list of one-column vectors each having same values. - CImgList get_split(const bool is_shared) const { - CImgList res; - if (is_empty()) return res; - T *p0 = _data, current = *p0; - cimg_for(*this,p,T) if (*p!=current) { res.insert(CImg(p0,1,p-p0,1,1,is_shared),~0U,is_shared); p0 = p; current = *p; } - res.insert(CImg(p0,1,end()-p0,1,1,is_shared),~0U,is_shared); - 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 is_queued(_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(is_queued,sizeQ,priority(x-1,y,z),x-1,y,z); - if (x+1=0 && !(*this)(x,y-1,z)) Q._priority_queue_insert(is_queued,sizeQ,priority(x,y-1,z),x,y-1,z); - if (y+1=0 && !(*this)(x,y,z-1)) Q._priority_queue_insert(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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(is_queued,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& is_queued, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) { - if (is_queued(x,y,z)) return false; - is_queued(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 joint bilateral filter. - /** - \param guide Image used to model the smoothing weights. - \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). - **/ - template - CImg& blur_bilateral(const CImg& guide, const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, - const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, - const bool interpolation_type=true) { - if (!is_sameXYZ(guide)) - throw CImgArgumentException(_cimg_instance - "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data); - if (is_empty()) return *this; - T m, M = guide.max_min(m); - if (m==M) return *this; - const float range = (float)(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, - _sigma_r = sigma_r>=0?sigma_r:-sigma_r*range/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) { - const CImg _guide = guide.get_shared_channel(c%guide._spectrum); - bgrid.fill(0); bgridw.fill(0); - cimg_forXYZ(*this,x,y,z) { - const T val = (*this)(x,y,z,c); - const float gval = (float)_guide(x,y,z); - const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)cimg::min(br-1.0f,(gval-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 float gval = (float)_guide(x,y,z), - X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)cimg::min(br-1.0f,(gval-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 float gval = (float)_guide(x,y,z); - const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)cimg::min(br-1.0f,(gval-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) { - const CImg _guide = guide.get_shared_channel(c%guide._spectrum); - bgrid.fill(0); - cimg_forXY(*this,x,y) { - const T val = (*this)(x,y,c); - const float gval = (float)_guide(x,y); - const int X = x*bx/_width, Y = y*by/_height, R = (int)cimg::min(br-1.0f,(gval-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 float gval = (float)_guide(x,y), - X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)cimg::min(br-1.0f,(gval-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 float gval = (float)_guide(x,y); - const int X = x*bx/_width, Y = y*by/_height, R = (int)cimg::min(br-1.0f,(gval-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 joint bilateral filter \newinstance. - template - CImg get_blur_bilateral(const CImg& guide, const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, - const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, - const bool interpolation_type=true) const { - return (+*this).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type); - } - - //! Blur image using the joint bilateral filter. - /** - \param guide Image used to model the smoothing weights. - \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. - **/ - template - CImg& blur_bilateral(const CImg& guide, const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, - const bool interpolation_type=true) { - const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; - return blur_bilateral(guide,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. - template - CImg get_blur_bilateral(const CImg& guide, const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, - const bool interpolation_type=true) const { - return (+*this).blur_bilateral(guide,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-5f + 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-5f + 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 Euclidean distance function 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 to a specified value, according to a custom metric (use dijkstra algorithm). - /** - \param value Reference value. - \param metric Field of distance potentials. - \param is_high_connectivity Tells if the algorithm uses low or high connectivity. - **/ - template - CImg& distance_dijkstra(const T value, const CImg& metric, const bool is_high_connectivity, CImg& return_path) { - return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); - } - - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. - template - CImg::type> get_distance_dijkstra(const T value, const CImg& metric, const bool is_high_connectivity, CImg& return_path) const { - if (is_empty()) return return_path.assign(); - if (!is_sameXYZ(metric)) - throw CImgArgumentException(_cimg_instance - "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) have incompatible dimensions.", - cimg_instance, - metric._width,metric._height,metric._depth,metric._spectrum); - typedef typename cimg::superset::type td; // Type used for computing cumulative distances. - CImg result(_width,_height,_depth,_spectrum), Q; - CImg is_queued(_width,_height,_depth,1); - if (return_path) return_path.assign(_width,_height,_depth,_spectrum); - - cimg_forC(*this,c) { - const CImg img = get_shared_channel(c); - const CImg met = metric.get_shared_channel(c%metric._spectrum); - CImg res = result.get_shared_channel(c); - CImg path = return_path?return_path.get_shared_channel(c):CImg(); - unsigned int sizeQ = 0; - - // Detect initial seeds. - is_queued.fill(0); - cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) { - Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z); - res(x,y,z) = 0; - if (path) path(x,y,z) = (to)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 td P = (td)-Q(0,0); - Q._priority_queue_remove(sizeQ); - - // Update neighbors. - td npot = 0; - if (x-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x-1,y,z)+P),x-1,y,z)) { - res(x-1,y,z) = npot; if (path) path(x-1,y,z) = (to)2; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y-1,z)+P),x,y-1,z)) { - res(x,y-1,z) = npot; if (path) path(x,y-1,z) = (to)8; - } - if (y+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z-1)+P),x,y,z-1)) { - res(x,y,z-1) = npot; if (path) path(x,y,z-1) = (to)32; - } - if (z+1=0 && y-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y-1,z)+P)),x-1,y-1,z)) { - res(x-1,y-1,z) = npot; if (path) path(x-1,y-1,z) = (to)10; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x+1,y-1,z)+P)),x+1,y-1,z)) { - res(x+1,y-1,z) = npot; if (path) path(x+1,y-1,z) = (to)9; - } - if (x-1>=0 && y+1=0) { // Diagonal neighbors on slice z-1. - if (x-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y,z-1)+P)),x-1,y,z-1)) { - res(x-1,y,z-1) = npot; if (path) path(x-1,y,z-1) = (to)34; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y-1,z-1)+P)),x,y-1,z-1)) { - res(x,y-1,z-1) = npot; if (path) path(x,y-1,z-1) = (to)40; - } - if (y+1=0 && y-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y-1,z-1)+P)),x-1,y-1,z-1)) { - res(x-1,y-1,z-1) = npot; if (path) path(x-1,y-1,z-1) = (to)42; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y-1,z-1)+P)),x+1,y-1,z-1)) { - res(x+1,y-1,z-1) = npot; if (path) path(x+1,y-1,z-1) = (to)41; - } - if (x-1>=0 && y+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y,z+1)+P)),x-1,y,z+1)) { - res(x-1,y,z+1) = npot; if (path) path(x-1,y,z+1) = (to)18; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y-1,z+1)+P)),x,y-1,z+1)) { - res(x,y-1,z+1) = npot; if (path) path(x,y-1,z+1) = (to)24; - } - if (y+1=0 && y-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y-1,z+1)+P)),x-1,y-1,z+1)) { - res(x-1,y-1,z+1) = npot; if (path) path(x-1,y-1,z+1) = (to)26; - } - if (x+1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y-1,z+1)+P)),x+1,y-1,z+1)) { - res(x+1,y-1,z+1) = npot; if (path) path(x+1,y-1,z+1) = (to)25; - } - if (x-1>=0 && y+1 - CImg& distance_dijkstra(const T value, const CImg& metric, const bool is_high_connectivity=false) { - return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this); - } - - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. - template - CImg get_distance_dijkstra(const T value, const CImg& metric, const bool is_high_connectivity=false) const { - CImg return_path; - return get_distance_dijkstra(value,metric,is_high_connectivity,return_path); - } - - //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). - /** - \param value Reference value. - \param metric Field of distance potentials. - **/ - template - CImg& distance_eikonal(const T value, const CImg& metric) { - return get_distance_eikonal(value,metric).move_to(*this); - } - - //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). - template - CImg get_distance_eikonal(const T value, const CImg& metric) const { - if (is_empty()) return *this; - if (!is_sameXYZ(metric)) - throw CImgArgumentException(_cimg_instance - "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have incompatible dimensions.", - cimg_instance, - metric._width,metric._height,metric._depth,metric._spectrum); - CImg result(_width,_height,_depth,_spectrum,cimg::type::max()),Q; - CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen. - - cimg_forC(*this,c) { - const CImg img = get_shared_channel(c); - const CImg met = metric.get_shared_channel(c%metric._spectrum); - CImg res = result.get_shared_channel(c); - unsigned int sizeQ = 0; - state.fill(-1); - - // Detect initial seeds. - Tfloat *ptr1 = res._data; char *ptr2 = state._data; - cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; } - - // Initialize seeds neighbors. - ptr2 = state._data; - cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) { - if (x-1>=0 && state(x-1,y,z)==-1) { - const Tfloat dist = res(x-1,y,z) = __distance_eikonal(res,met(x-1,y,z),x-1,y,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x-1,y,z); - } - if (x+1=0 && state(x,y-1,z)==-1) { - const Tfloat dist = res(x,y-1,z) = __distance_eikonal(res,met(x,y-1,z),x,y-1,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y-1,z); - } - if (y+1=0 && state(x,y,z-1)==-1) { - const Tfloat dist = res(x,y,z-1) = __distance_eikonal(res,met(x,y,z-1),x,y,z-1); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z-1); - } - if (z+1=0) { - if (x-1>=0 && state(x-1,y,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x-1,y,z),x-1,y,z); - if (dist=0 && state(x,y-1,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y-1,z),x,y-1,z); - if (dist=0 && state(x,y,z-1)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y,z-1),x,y,z-1); - if (dist& res, const Tfloat P, const int x=0, const int y=0, const int z=0) const { - const T M = cimg::type::max(); - T T1 = cimg::min(x-1>=0?res(x-1,y,z):M,x+11) { // 3d. - T - T2 = cimg::min(y-1>=0?res(x,y-1,z):M,y+1=0?res(x,y,z-1):M,z+1T2) cimg::swap(T1,T2); - if (T2>T3) cimg::swap(T2,T3); - if (T1>T2) cimg::swap(T1,T2); - if (P<=0) return (Tfloat)T1; - if (T31) { // 2d. - T T2 = cimg::min(y-1>=0?res(x,y-1,z):M,y+1T2) cimg::swap(T1,T2); - if (P<=0) return (Tfloat)T1; - if (T2 - void _eik_priority_queue_insert(CImg& state, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) { - if (state(x,y,z)>0) return; - state(x,y,z) = 0; - 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)); - } - } - - //! Compute distance function to 0-valued isophotes, using the Eikonal PDE. - /** - \param nb_iterations Number of PDE iterations. - \param band_size Size of the narrow band. - \param time_step Time step of the PDE iterations. - **/ - CImg& 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 - cimg::mutex(12); - 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); - cimg::mutex(12,0); -#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. - \param nb_threads Number of parallel threads used for the computation. Use \c 0 to set this to the number of available cpus. - **/ - static void FFT(CImg& real, CImg& imag, const bool is_invert=false, const unsigned int nb_threads=0) { - 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 - cimg::mutex(12); -#ifndef cimg_use_fftw3_singlethread - const unsigned int _nb_threads = nb_threads?nb_threads:cimg::nb_cpus(); - static int fftw_st = fftw_init_threads(); - cimg::unused(fftw_st); - fftw_plan_with_nthreads(_nb_threads); -#else - cimg::unused(nb_threads); -#endif - 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 const 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 const 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. - \param full_check Tells if full checking of the 3d object must be performed. - **/ - template - CImg& object3dtoCImg3d(const CImgList& primitives, - const CImgList& colors, - const to& opacities, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this); - } - - //! Convert 3d object into a CImg3d representation \overloading. - template - CImg& object3dtoCImg3d(const CImgList& primitives, - const CImgList& colors, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this); - } - - //! Convert 3d object into a CImg3d representation \overloading. - template - CImg& object3dtoCImg3d(const CImgList& primitives, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,full_check).move_to(*this); - } - - //! Convert 3d object into a CImg3d representation \overloading. - CImg& object3dtoCImg3d(const bool full_check=true) { - return get_object3dtoCImg3d(full_check).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 bool full_check=true) const { - char error_message[1024] = { 0 }; - if (!is_object3d(primitives,colors,opacities,full_check,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 bool full_check=true) const { - CImgList opacities; - return get_object3dtoCImg3d(primitives,colors,opacities,full_check); - } - - //! Convert 3d object into a CImg3d representation \overloading. - template - CImg get_object3dtoCImg3d(const CImgList& primitives, - const bool full_check=true) const { - CImgList colors, opacities; - return get_object3dtoCImg3d(primitives,colors,opacities,full_check); - } - - //! Convert 3d object into a CImg3d representation \overloading. - CImg get_object3dtoCImg3d(const bool full_check=true) 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,full_check); - } - - //! 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. - \param full_check Tells if full checking of the 3d object must be performed. - **/ - template - CImg& CImg3dtoobject3d(CImgList& primitives, - CImgList& colors, - CImgList& opacities, - const bool full_check=true) { - return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this); - } - - //! Convert CImg3d representation into a 3d object \newinstance. - template - CImg get_CImg3dtoobject3d(CImgList& primitives, - CImgList& colors, - CImgList& opacities, - const bool full_check=true) const { - char error_message[1024] = { 0 }; - if (!is_CImg3d(full_check,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, - const float brightness, - const float nopacity, const float copacity, const unsigned long whd) { - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); - const int nx0 = x0>0?x0:0, nx1 = x1=0) { - const tc *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; - } - - //! 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) { - cimg_init_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) cimg_draw_scanline(xl,xr,y,color,opacity,nbrightness); - else - _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) cimg_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 = 1, p = 0; - int cx = npoints(0,0) = (int)points(0,0), cy = npoints(0,1) = (int)points(0,1); - const int cx0 = cx, cy0 = cy; - for (p = 1; p 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 cimg_draw_scanline(xmin,xmax,ymin,color,opacity,1); - 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. - 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 cimg_draw_scanline(xmin,xmax,ymin,color,1,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) { - cimg_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); - cimg_init_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) cimg_draw_scanline(xmin,xmax,y,color,opacity,1); - else { - if (first_line) { - if (y0-yb>=0) cimg_draw_scanline(xmin,xmax,y,color,opacity,1); - 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); - cimg_init_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,false); - } - - //! 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,false); - } - - //! 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,false); - } - - //! 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,23,53,103, 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); - const CImgList& font = CImgList::font(font_height,true); - _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); - return *this; - } - - //! 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, - const bool is_native_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); - const bool _is_empty = is_empty(); - 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,is_native_font?1:font[0]._spectrum,0); - } - - int x = x0, y = y0; - for (unsigned int i = 0; i letter = font[c]; - if (letter) { - if (is_native_font && _spectrum>letter._spectrum) letter.resize(-100,-100,1,_spectrum,0,2); - const unsigned int cmin = cimg::min(_spectrum,letter._spectrum); - if (foreground_color) for (unsigned int c = 0; c - 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,23,53,103, 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,23,53,103, 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,23,53,103, 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 delta = 1<1; delta>>=1) { - const int delta2 = delta>>1; - const float r = alpha*delta + beta; - - // Square step. - for (int y0 = 0; y0M?M:val); - } - - // Diamond steps. - for (int y = -delta2; yM?M:val); - } - for (int y0 = 0; y0M?M:val); - } - for (int y = -delta2; 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); - if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety. - -#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 - 1)/2*(1 + dlx/nl), - nly = (default_light_texture._height - 1)/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; - } - } - - if (render_type==5) cimg::mutex(10,0); - 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().show_mouse(); - - 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-1)/2)%_width), - Y0 = (int)((XYZ?XYZ[1]:(_height-1)/2)%_height), - Z0 = (int)((XYZ?XYZ[2]:(_depth-1)/2)%_depth), - X1 =-1, Y1 = -1, Z1 = -1, - X3d = -1, Y3d = -1, - oX3d = X3d, oY3d = -1, - omx = -1, omy = -1; - float X = -1, Y = -1, Z = -1; - unsigned int old_button = 0, key = 0; - - bool shape_selected = false, text_down = false, visible_cursor = true; - static CImg pose3d; - static bool is_view3d = false, is_axes = true; - 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 - int - mx = disp.mouse_x(), - my = disp.mouse_y(); - - const float - mX = mx<0?-1.0f:(float)mx*(width()+(depth()>1?depth():0))/disp.width(), - mY = my<0?-1.0f:(float)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 = (float)(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::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign(); - } 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 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - 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) { - (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp); - visu0.save(filename); - (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,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); - (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp); - save(filename); - (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,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 = -1; 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!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); - X1 = (int)X; Y1 = (int)Y; Z1 = (int)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!=(int)Z) visu0.assign(); Z1 = (int)Z; break; - case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; - case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; - } - } - if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume. - if (phase) { - if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); - X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; - } else { - if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); - X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; - } - } - if (disp.button()&4) { // Reset positions. - X = (float)X0; Y = (float)Y0; Z = (float)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 = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); - visu0.assign(); break; - case 2 : - if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); - visu0.assign(); break; - case 3 : - if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(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 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; starting_area = area; ++phase; - } break; - case 1 : - if (area==starting_area) { - X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; - } else if (!(disp.button()&1)) { X = (float)X0; Y = (float)Y0; Z = (float)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 = -1; 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 (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { - - if (!visu0) { // Create image of projected planes. - __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(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-1),-0.5f*(_height-1),-0.5f*(_depth-1)).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; - - if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} - else { - if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} - else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} - const int d = (_depth>1)?_depth:0; - int - w = disp.width(), W = width() + d, - h = disp.height(), H = height() + d, - _xp = (int)X*w/W, xp = _xp + (_xp*W/w!=(int)X?1:0), - _yp = (int)Y*h/H, yp = _yp + (_yp*H/h!=(int)Y?1:0), - _xn = (int)(X+1)*w/W-1, xn = _xn + ((_xn+1)*W/w!=(int)X+1?1:0), - _yn = (int)(Y+1)*h/H-1, yn = _yn + ((_yn+1)*H/h!=(int)Y+1?1:0), - _zxp = ((int)Z+width())*w/W, zxp = _zxp + (_zxp*W/w!=(int)Z+width()?1:0), - _zyp = ((int)Z+height())*h/H, zyp = _zyp + (_zyp*H/h!=(int)Z+height()?1:0), - _zxn = ((int)Z+width()+1)*w/W-1, zxn = _zxn + ((_zxn+1)*W/w!=(int)Z+width()+1?1:0), - _zyn = ((int)Z+height()+1)*h/H-1, zyn = _zyn + ((_zyn+1)*H/h!=(int)Z+height()+1?1:0), - _xM = width()*w/W-1, xM = _xM + ((_xM+1)*W/w!=width()?1:0), - _yM = height()*h/H-1, yM = _yM + ((_yM+1)*H/h!=height()?1:0), - xc = (xp + xn)/2, - yc = (yp + yn)/2, - zxc = (zxp + zxn)/2, - zyc = (zyp + zyn)/2, - xf = (int)(X*w/W), - yf = (int)(Y*h/H), - zxf = (int)((Z+width())*w/W), - zyf = (int)((Z+height())*h/H); - - if (is_axes) { // Draw axes. - visu.draw_line(0,yf,visu.width()-1,yf,foreground_color,0.7f,0xFF00FF00). - draw_line(0,yf,visu.width()-1,yf,background_color,0.7f,0x00FF00FF). - draw_line(xf,0,xf,visu.height()-1,foreground_color,0.7f,0xFF00FF00). - draw_line(xf,0,xf,visu.height()-1,background_color,0.7f,0x00FF00FF); - if (_depth>1) - visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00). - draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF). - draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00). - draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF); - } - - // Draw box cursor. - if (xn-xp>=4 && yn-yp>=4) visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). - draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); - if (_depth>1) { - if (yn-yp>=4 && zxn-zxp>=4) visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). - draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); - if (xn-xp>=4 && zyn-zyp>=4) visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). - draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); - } - - // Draw selection. - if (phase) { - const int - _xp0 = X0*w/W, xp0 = _xp0 + (_xp0*W/w!=X0?1:0), - _yp0 = Y0*h/H, yp0 = _yp0 + (_yp0*H/h!=Y0?1:0), - _xn0 = (X0+1)*w/W-1, xn0 = _xn0 + ((_xn0+1)*W/w!=X0+1?1:0), - _yn0 = (Y0+1)*h/H-1, yn0 = _yn0 + ((_yn0+1)*H/h!=Y0+1?1:0), - _zxp0 = (Z0+width())*w/W, zxp0 = _zxp0 + (_zxp0*W/w!=Z0+width()?1:0), - _zyp0 = (Z0+height())*h/H, zyp0 = _zyp0 + (_zyp0*H/h!=Z0+height()?1:0), - _zxn0 = (Z0+width()+1)*w/W-1, zxn0 = _zxn0 + ((_zxn0+1)*W/w!=Z0+width()+1?1:0), - _zyn0 = (Z0+height()+1)*h/H-1, zyn0 = _zyn0 + ((_zyn0+1)*H/h!=Z0+height()+1?1:0), - xc0 = (xp0 + xn0)/2, - yc0 = (yp0 + yn0)/2, - zxc0 = (zxp0 + zxn0)/2, - zyc0 = (zyp0 + zyn0)/2; - - switch (feature_type) { - case 1 : { - visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555). - draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA); - if (d) { - visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555). - draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA). - draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555). - draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA); - } - } break; - case 2 : { - visu.draw_rectangle(X0=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+(int)X,origY+(int)Y,origZ+(int)Z); - else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+(int)X,origY+(int)Y); - char *ctext = text + std::strlen(text), *const ltext = text + 512; - for (unsigned int c = 0; c<_spectrum && ctext::format(),cimg::type::format((*this)((int)X,(int)Y,(int)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(); } - omx = mx; omy = my; - } - - // 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(); - if (!visible_cursor) disp.show_mouse(); - disp._normalization = old_normalization; - disp._is_resized = old_is_resized; - if (key!=~0U) disp.set_key(key); - return res; - } - - // Return a visualizable uchar8 image for display routines. - CImg __get_select(const CImgDisplay& disp, const int normalization, const int x, const int y, const int z) const { - if (is_empty()) return CImg(1,1,1,1,0); - const CImg crop = get_shared_channels(0,cimg::min(2,spectrum()-1)); - CImg img2d; - if (_depth>1) crop.get_projections2d(x,y,z).move_to(img2d); - else CImg(crop,false).move_to(img2d); - - if (cimg::type::is_float()) { // Check for inf and nan values. - bool is_inf = false, is_nan = false; - cimg_for(img2d,ptr,Tuchar) - if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } - else if (cimg::type::is_nan(*ptr)) { is_nan = true; break; } - if (is_inf || is_nan) { - T m0 = cimg::type::max(), M0 = cimg::type::min(); - if (!normalization) { m0 = 0; M0 = 255; } - else if (normalization==2) { m0 = (T)disp._min; M0 = (T)disp._max; } - else cimg_for(img2d,ptr,Tuchar) if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { if (*ptrM0) M0 = *ptr; } - const T - val_minf = (normalization==1 || normalization==3)?m0-(M0-m0)*20-1:m0, - val_pinf = (normalization==1 || normalization==3)?M0+(M0-m0)*20+1:M0; - if (is_nan) cimg_for(img2d,ptr,Tuchar) if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace nan values. - if (is_inf) cimg_for(img2d,ptr,Tuchar) if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values. - } - } - - switch (normalization) { - case 1 : img2d.normalize(0,255); break; - case 2 : { - const float m = disp._min, M = disp._max; - (img2d-=m)*=255.0f/(M-m>0?M-m:1); - } break; - case 3 : - if (cimg::type::is_float()) img2d.normalize(0,255); - else { - const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); - (img2d-=m)*=255.0f/(M-m>0?M-m:1); - } break; - } - - if (img2d.spectrum()==2) img2d.channels(0,2); - return img2d; - } - - //! 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), - header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<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); - } - if (header_size>40) std::fseek(nfile, header_size - 40, SEEK_CUR); - - 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 (~0U for max). - \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 (~0U for max). - \param y1 Y-coordinate of the ending sub-image vertex (~0U for max). - \param z1 Z-coordinate of the ending sub-image vertex (~0U for max). - \param c1 C-coordinate of the ending sub-image vertex (~0U for max). - \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. - \param offset Starting offset of the read in the specified file. - **/ - 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, - const unsigned long offset=0) { - return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! 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, - const unsigned long offset=0) { - return CImg().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! 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, - const unsigned long offset=0) { - return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! 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, - const unsigned long offset=0) { - return CImg().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - 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, - const unsigned long offset) { - 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); - } - std::fseek(nfile,(long)offset,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 stop_flag = false; - while (!stop_flag) { - 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) stop_flag = 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; - 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("%s",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); - - if (!is_first_select) { _XYZ[0] = (x1-x0)/2; _XYZ[1] = (y1-y0)/2; _XYZ[2] = (z1-z0)/2; } - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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=-5e8f, - 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 objects - 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+Zoff,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,0.7f,13).display(disp); - visu.save(filename); - (+visu).draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,0.7f,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,0.7f,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,0.7f,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,0.7f,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,0.7f,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,0.7f,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+Zoff,specular_lightness,specular_shininess, - sprite_scale); - board.saveEPS(filename); - (+visu).draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,0.7f,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,0.7f,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+Zoff,specular_lightness,specular_shininess, - sprite_scale); - board.saveSVG(filename); - (+visu).draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,0.7f,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 { - return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); - } - - //! 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 { - CImgDisplay disp; - return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); - } - - const CImg& _display_graph(CImgDisplay &disp, 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); - if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title(title?"%s":"CImg<%s>",title?title: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; - } - - //! Save image as a file. - /** - \param filename Filename, as a C-string. - \param number When positive, represents an index added to the filename. Otherwise, no number is added. - \param digits Number of digits used for adding the number 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 unsigned int digits=6) 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,digits,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& 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() { - CImg res(40,38,1,3); - 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 image \c img has been inserted at the end \const. - template - CImgList operator,(const CImg& img) const { - return (+*this).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 a copy of the list instance, where all elements of input list \c list have been inserted at the end \const. - template - CImgList& operator,(const CImgList& list) const { - return (+*this).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; - CImg res; - src.__get_select(disp,old_normalization,(src._width-1)/2,(src._height-1)/2,(src._depth-1)/2).move_to(res); - 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-1)/2,(src._height-1)/2,(src._depth-1)/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,0xAAAAAAAA); - } - 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 (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13, - orig + indice0, - _data[orig+indice0]._width, - _data[orig+indice0]._height, - _data[orig+indice0]._depth, - _data[orig+indice0]._spectrum); - 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) { - (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp); - visu0.save(filename); - (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,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); - (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp); - save(filename); - (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,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 (~0U for max). - \param n1 Ending index of images to read (~0U for max). - \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 (~0U for max). - \param y1 Ending Y-coordinates of image regions to read (~0U for max). - \param z1 Ending Z-coordinates of image regions to read (~0U for max). - \param c1 Ending C-coordinates of image regions to read (~0U for max). - **/ - 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 \overloading. - 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 || ny0>=H || nz0>=D || nc0>=C) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ - else { \ - const unsigned int \ - _nx1 = nx1==~0U?W-1:nx1, \ - _ny1 = ny1==~0U?H-1:ny1, \ - _nz1 = nz1==~0U?D-1:nz1, \ - _nc1 = nc1==~0U?C-1:nc1; \ - if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \ - throw CImgArgumentException(_cimglist_instance \ - "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \ - "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \ - cimglist_instance, \ - n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \ - CImg raw(1 + _nx1 - nx0); \ - CImg &img = _data[l - nn0]; \ - img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ - T *ptrd = img._data; \ - const unsigned int skipvb = nc0*W*H*D*sizeof(Tss); \ - if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ - for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \ - const unsigned int skipzb = nz0*W*H*sizeof(Tss); \ - if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \ - for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \ - const unsigned int skipyb = ny0*W*sizeof(Tss); \ - if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \ - for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \ - const unsigned int skipxb = nx0*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); - unsigned int - nn0 = cimg::min(n0,n1), nn1 = cimg::max(n0,n1), - nx0 = cimg::min(x0,x1), nx1 = cimg::max(x0,x1), - ny0 = cimg::min(y0,y1), ny1 = cimg::max(y0,y1), - nz0 = cimg::min(z0,z1), nz1 = cimg::max(z0,z1), - nc0 = cimg::min(c0,c1), nc1 = cimg::max(c0,c1); - - 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; - nn1 = n1==~0U?N-1:n1; - if (nn1>=N) - throw CImgArgumentException(_cimglist_instance - "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " - "because file '%s' contains only %u images.", - cimglist_instance, - n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); - 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 stop_flag = 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; !stop_flag && 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)) { - stop_flag = 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())) { - stop_flag = 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 (stop_flag && 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 stop_flag = false; !stop_flag; ++i) { - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i); - CImg img; - try { img.load_pnm(filetmp2); } - catch (CImgException&) { stop_flag = 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 stop_flag = false; !stop_flag; ++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&) { stop_flag = 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 When positive, represents an index added to the filename. Otherwise, no number is added. - \param digits Number of digits used for adding the number to the filename. - **/ - const CImgList& save(const char *const filename, const int number=-1, const unsigned int digits=6) 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,digits,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) { - if (!font_height) return CImgList::empty(); - cimg::mutex(11); - - // Decompress nearest base font data if needed. - const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; - const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 }, data_Ms[] = { 86,79,57,47 }; - const unsigned int data_ind = font_height<=13?0:font_height<=23?1:font_height<=53?2:3; - static CImg base_fonts[4]; - CImg &base_font = base_fonts[data_ind]; - if (!base_font) { - const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind]; - base_font.assign(256*w,h); - const char *data_font = data_fonts[data_ind]; - unsigned char *ptrd = base_font; - const unsigned char *const ptrde = base_font.end(); - - // Special case needed for 90x103 to avoid MS compiler limit with big strings. - CImg data90x103; - if (!data_font) { - ((CImg(cimg::_data_font90x103[0],(unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true), - CImg(cimg::_data_font90x103[1],(unsigned int)std::strlen(cimg::_data_font90x103[1])+1,1,1,1,true))>'x').move_to(data90x103); - data_font = data90x103.data(); - } - - // Uncompress font data (decode RLE). - for (const char *ptrs = data_font; *ptrs; ++ptrs) { - const int c = *ptrs-M-32, v = c>=0?255:0, n = c>=0?c:-c; - if (ptrd+n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } - else { std::memset(ptrd,v,ptrde-ptrd); break; } - } - } - - // Find optimal font cache location to return. - static CImgList fonts[16]; - static bool is_variable_widths[16] = { 0 }; - unsigned int ind = ~0U; - for (int i = 0; i<16; ++i) - if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) { ind = i; break; } // Found empty slot or cached font. - if (ind==~0U) { // No empty slots nor existing font in cache. - std::memmove(fonts,fonts+1,15*sizeof(CImgList)); - std::memmove(is_variable_widths,is_variable_widths+1,15*sizeof(bool)); - std::memset(fonts+(ind=15),0,sizeof(CImgList)); // Free a slot in cache for new font. - } - CImgList &font = fonts[ind]; - - // Render requested font. - if (!font) { - const unsigned int padding_x = font_height<33?1:font_height<53?2:font_height<103?3:4; - is_variable_widths[ind] = is_variable_width; - font = base_font.get_split('x',256); - if (font_height!=font[0]._height) - cimglist_for(font,l) - font[l].resize(cimg::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100, - font[0]._height>font_height?2:5); - if (is_variable_width) font.crop_font(); - cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5); - font.insert(256,0); - cimglist_for_in(font,0,255,l) font[l].assign(font[l+256]._width,font[l+256]._height,1,3,1); - } - cimg::mutex(11,0); - return font; - } - - //! 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 { - - // Implement a tic/toc mechanism to display elapsed time of algorithms. - inline unsigned long tictoc(const bool is_tic) { - cimg::mutex(2); - static CImg times(64); - static unsigned int pos = 0; - const unsigned long t1 = cimg::time(); - if (is_tic) { // Tic. - times[pos++] = t1; - if (pos>=times._width) - throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); - cimg::mutex(2,0); - return t1; - } - // Toc. - if (!pos) - throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); - const unsigned long - t0 = times[--pos], - dt = t1>=t0?(t1-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]%*sElapsed time: %u ms%s\n",cimg::t_red,1+2*pos,"",ems,cimg::t_normal); - else { - if (!edays && !ehours && !emin) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",cimg::t_red,1+2*pos,"",esec,ems,cimg::t_normal); - else { - if (!edays && !ehours) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",cimg::t_red,1+2*pos,"",emin,esec,ems,cimg::t_normal); - else{ - if (!edays) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",cimg::t_red,1+2*pos,"",ehours,emin,esec,ems,cimg::t_normal); - else{ - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",cimg::t_red,1+2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal); - } - } - } - } - cimg::mutex(2,0); - return dt; - } - - //! 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",gray,0,1,13,msg)*=-1)+=200).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 stop_flag = false, refresh = false; - int oselected = -1, oclicked = -1, selected = -1, clicked = -1; - while (!disp.is_closed() && !stop_flag) { - 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) stop_flag = true; - - if (disp.key()) { - oselected = selected; - switch (disp.key()) { - case cimg::keyESC : selected=-1; stop_flag = true; break; - case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = 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); - } - - template - inline CImg::type> eval(const char *const expression, const CImg& xyzc) { - static const CImg empty; - return empty.eval(expression,xyzc); - } - - // 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/deps/CImg/Licence_CeCILL-C_V1-en.txt b/deps/CImg/Licence_CeCILL-C_V1-en.txt deleted file mode 100644 index 2e9ffba..0000000 --- a/deps/CImg/Licence_CeCILL-C_V1-en.txt +++ /dev/null @@ -1,508 +0,0 @@ - - CeCILL-C FREE SOFTWARE LICENSE AGREEMENT - - - Notice - -This Agreement is a Free Software license agreement that is the result -of discussions between its authors in order to ensure compliance with -the two main principles guiding its drafting: - - * firstly, compliance with the principles governing the distribution - of Free Software: access to source code, broad rights granted to - users, - * secondly, the election of a governing law, French law, with which - it is conformant, both as regards the law of torts and - intellectual property law, and the protection that it offers to - both authors and holders of the economic rights over software. - -The authors of the CeCILL-C (for Ce[a] C[nrs] I[nria] L[logiciel] L[ibre]) -license are: - -Commissariat à l'Energie Atomique - CEA, a public scientific, technical -and industrial research establishment, having its principal place of -business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. - -Centre National de la Recherche Scientifique - CNRS, a public scientific -and technological establishment, having its principal place of business -at 3 rue Michel-Ange, 75794 Paris cedex 16, France. - -Institut National de Recherche en Informatique et en Automatique - -INRIA, a public scientific and technological establishment, having its -principal place of business at Domaine de Voluceau, Rocquencourt, BP -105, 78153 Le Chesnay cedex, France. - - - Preamble - -The purpose of this Free Software license agreement is to grant users the -right to modify and re-use the software governed by this license. - -The exercising of this right is conditional on the obligation to make -available to the community the modifications made to the source code of the -software so as to contribute to its evolution. - -In consideration of access to the source code and the rights to copy, -modify and redistribute granted by the license, users are provided only -with a limited warranty and the software's author, the holder of the -economic rights, and the successive licensors only have limited liability. - -In this respect, the risks associated with loading, using, modifying -and/or developing or reproducing the software by the user are brought to -the user's attention, given its Free Software status, which may make it -complicated to use, with the result that its use is reserved for -developers and experienced professionals having in-depth computer -knowledge. Users are therefore encouraged to load and test the suitability -of the software as regards their requirements in conditions enabling the -security of their systems and/or data to be ensured and, more generally, to -use and operate it in the same conditions of security. This Agreement may be -freely reproduced and published, provided it is not altered, and that no -provisions are either added or removed herefrom. - -This Agreement may apply to any or all software for which the holder of -the economic rights decides to submit the use thereof to its provisions. - - - Article 1 - DEFINITIONS - -For the purpose of this Agreement, when the following expressions -commence with a capital letter, they shall have the following meaning: - -Agreement: means this license agreement, and its possible subsequent -versions and annexes. - -Software: means the software in its Object Code and/or Source Code form -and, where applicable, its documentation, "as is" when the Licensee -accepts the Agreement. - -Initial Software: means the Software in its Source Code and possibly its -Object Code form and, where applicable, its documentation, "as is" when -it is first distributed under the terms and conditions of the Agreement. - -Modified Software: means the Software modified by at least one Integrated -Contribution. - -Source Code: means all the Software's instructions and program lines to -which access is required so as to modify the Software. - -Object Code: means the binary files originating from the compilation of -the Source Code. - -Holder: means the holder(s) of the economic rights over the Initial -Software. - -Licensee: means the Software user(s) having accepted the Agreement. - -Contributor: means a Licensee having made at least one Integrated -Contribution. - -Licensor: means the Holder, or any other individual or legal entity, who -distributes the Software under the Agreement. - -Integrated Contribution: means any or all modifications, corrections, -translations, adaptations and/or new functions integrated into the Source -Code by any or all Contributors. - -Related Module: means a set of sources files including their documentation -that, without modification to the Source Code, enables supplementary -functions or services in addition to those offered by the Software. - -Derivative Software: means any combination of the Software, modified or not, -and of a Related Module. - -Parties: mean both the Licensee and the Licensor. - -These expressions may be used both in singular and plural form. - - - Article 2 - PURPOSE - -The purpose of the Agreement is the grant by the Licensor to the -Licensee of a non-exclusive, transferable and worldwide license for the -Software as set forth in Article 5 hereinafter for the whole term of the -protection granted by the rights over said Software. - - - Article 3 - ACCEPTANCE - -3.1 The Licensee shall be deemed as having accepted the terms and -conditions of this Agreement upon the occurrence of the first of the -following events: - - * (i) loading the Software by any or all means, notably, by - downloading from a remote server, or by loading from a physical - medium; - * (ii) the first time the Licensee exercises any of the rights - granted hereunder. - -3.2 One copy of the Agreement, containing a notice relating to the -characteristics of the Software, to the limited warranty, and to the -fact that its use is restricted to experienced users has been provided -to the Licensee prior to its acceptance as set forth in Article 3.1 -hereinabove, and the Licensee hereby acknowledges that it has read and -understood it. - - - Article 4 - EFFECTIVE DATE AND TERM - - - 4.1 EFFECTIVE DATE - -The Agreement shall become effective on the date when it is accepted by -the Licensee as set forth in Article 3.1. - - - 4.2 TERM - -The Agreement shall remain in force for the entire legal term of -protection of the economic rights over the Software. - - - Article 5 - SCOPE OF RIGHTS GRANTED - -The Licensor hereby grants to the Licensee, who accepts, the following -rights over the Software for any or all use, and for the term of the -Agreement, on the basis of the terms and conditions set forth hereinafter. - -Besides, if the Licensor owns or comes to own one or more patents -protecting all or part of the functions of the Software or of its -components, the Licensor undertakes not to enforce the rights granted by -these patents against successive Licensees using, exploiting or -modifying the Software. If these patents are transferred, the Licensor -undertakes to have the transferees subscribe to the obligations set -forth in this paragraph. - - - 5.1 RIGHT OF USE - -The Licensee is authorized to use the Software, without any limitation -as to its fields of application, with it being hereinafter specified -that this comprises: - - 1. permanent or temporary reproduction of all or part of the Software - by any or all means and in any or all form. - 2. loading, displaying, running, or storing the Software on any or - all medium. - 3. entitlement to observe, study or test its operation so as to - determine the ideas and principles behind any or all constituent - elements of said Software. This shall apply when the Licensee - carries out any or all loading, displaying, running, transmission - or storage operation as regards the Software, that it is entitled - to carry out hereunder. - - - 5.2 RIGHT OF MODIFICATION - -The right of modification includes the right to translate, adapt, arrange, -or make any or all modifications to the Software, and the right to -reproduce the resulting Software. It includes, in particular, the right -to create a Derivative Software. - -The Licensee is authorized to make any or all modification to the -Software provided that it includes an explicit notice that it is the -author of said modification and indicates the date of the creation thereof. - - - 5.3 RIGHT OF DISTRIBUTION - -In particular, the right of distribution includes the right to publish, -transmit and communicate the Software to the general public on any or -all medium, and by any or all means, and the right to market, either in -consideration of a fee, or free of charge, one or more copies of the -Software by any means. - -The Licensee is further authorized to distribute copies of the modified -or unmodified Software to third parties according to the terms and -conditions set forth hereinafter. - - - 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION - -The Licensee is authorized to distribute true copies of the Software in -Source Code or Object Code form, provided that said distribution -complies with all the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - - 2. a notice relating to the limitation of both the Licensor's - warranty and liability as set forth in Articles 8 and 9, - -and that, in the event that only the Object Code of the Software is -redistributed, the Licensee allows effective access to the full Source Code -of the Software at a minimum during the entire period of its distribution -of the Software, it being understood that the additional cost of acquiring -the Source Code shall not exceed the cost of transferring the data. - - - 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE - -When the Licensee makes an Integrated Contribution to the Software, the terms -and conditions for the distribution of the resulting Modified Software become -subject to all the provisions of this Agreement. - -The Licensee is authorized to distribute the Modified Software, in source -code or object code form, provided that said distribution complies with all -the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - 2. a notice relating to the limitation of both the Licensor's warranty and - liability as set forth in Articles 8 and 9, - -and that, in the event that only the object code of the Modified Software is -redistributed, the Licensee allows effective access to the full source code -of the Modified Software at a minimum during the entire period of its -distribution of the Modified Software, it being understood that the -additional cost of acquiring the source code shall not exceed the cost of -transferring the data. - - 5.3.3 DISTRIBUTION OF DERIVATIVE SOFTWARE - -When the Licensee creates Derivative Software, this Derivative Software may -be distributed under a license agreement other than this Agreement, subject -to compliance with the requirement to include a notice concerning the rights -over the Software as defined in Article 6.4. In the event the creation of the -Derivative Software required modification of the Source Code, the Licensee -undertakes that: - - 1. the resulting Modified Software will be governed by this Agreement, - 2. the Integrated Contributions in the resulting Modified Software will be - clearly identified and documented, - 3. the Licensee will allow effective access to the source code of the - Modified Software, at a minimum during the entire period of - distribution of the Derivative Software, such that such modifications - may be carried over in a subsequent version of the Software; it being - understood that the additional cost of purchasing the source code of - the Modified Software shall not exceed the cost of transferring the - data. - - - 5.3.4 COMPATIBILITY WITH THE CeCILL LICENSE - -When a Modified Software contains an Integrated Contribution subject to the -CeCill license agreement, or when a Derivative Software contains a Related -Module subject to the CeCill license agreement, the provisions set forth in -the third item of Article 6.4 are optional. - - - Article 6 - INTELLECTUAL PROPERTY - - - 6.1 OVER THE INITIAL SOFTWARE - -The Holder owns the economic rights over the Initial Software. Any or -all use of the Initial Software is subject to compliance with the terms -and conditions under which the Holder has elected to distribute its work -and no one shall be entitled to modify the terms and conditions for the -distribution of said Initial Software. - -The Holder undertakes that the Initial Software will remain ruled at -least by the current license, for the duration set forth in Article 4.2. - - - 6.2 OVER THE INTEGRATED CONTRIBUTIONS - -A Licensee who develops an Integrated Contribution is the owner of the -intellectual property rights over this Contribution as defined by -applicable law. - - - 6.3 OVER THE RELATED MODULES - -A Licensee who develops an Related Module is the owner of the -intellectual property rights over this Related Module as defined by -applicable law and is free to choose the type of agreement that shall -govern its distribution under the conditions defined in Article 5.3.3. - - - 6.4 NOTICE OF RIGHTS - -The Licensee expressly undertakes: - - 1. not to remove, or modify, in any manner, the intellectual property - notices attached to the Software; - 2. to reproduce said notices, in an identical manner, in the copies - of the Software modified or not; - 3. to ensure that use of the Software, its intellectual property - notices and the fact that it is governed by the Agreement is - indicated in a text that is easily accessible, specifically from - the interface of any Derivative Software. - -The Licensee undertakes not to directly or indirectly infringe the -intellectual property rights of the Holder and/or Contributors on the -Software and to take, where applicable, vis-à-vis its staff, any and all -measures required to ensure respect of said intellectual property rights -of the Holder and/or Contributors. - - - Article 7 - RELATED SERVICES - -7.1 Under no circumstances shall the Agreement oblige the Licensor to -provide technical assistance or maintenance services for the Software. - -However, the Licensor is entitled to offer this type of services. The -terms and conditions of such technical assistance, and/or such -maintenance, shall be set forth in a separate instrument. Only the -Licensor offering said maintenance and/or technical assistance services -shall incur liability therefor. - -7.2 Similarly, any Licensor is entitled to offer to its licensees, under -its sole responsibility, a warranty, that shall only be binding upon -itself, for the redistribution of the Software and/or the Modified -Software, under terms and conditions that it is free to decide. Said -warranty, and the financial terms and conditions of its application, -shall be subject of a separate instrument executed between the Licensor -and the Licensee. - - - Article 8 - LIABILITY - -8.1 Subject to the provisions of Article 8.2, the Licensee shall be -entitled to claim compensation for any direct loss it may have suffered -from the Software as a result of a fault on the part of the relevant -Licensor, subject to providing evidence thereof. - -8.2 The Licensor's liability is limited to the commitments made under -this Agreement and shall not be incurred as a result of in particular: -(i) loss due the Licensee's total or partial failure to fulfill its -obligations, (ii) direct or consequential loss that is suffered by the -Licensee due to the use or performance of the Software, and (iii) more -generally, any consequential loss. In particular the Parties expressly -agree that any or all pecuniary or business loss (i.e. loss of data, -loss of profits, operating loss, loss of customers or orders, -opportunity cost, any disturbance to business activities) or any or all -legal proceedings instituted against the Licensee by a third party, -shall constitute consequential loss and shall not provide entitlement to -any or all compensation from the Licensor. - - - Article 9 - WARRANTY - -9.1 The Licensee acknowledges that the scientific and technical -state-of-the-art when the Software was distributed did not enable all -possible uses to be tested and verified, nor for the presence of -possible defects to be detected. In this respect, the Licensee's -attention has been drawn to the risks associated with loading, using, -modifying and/or developing and reproducing the Software which are -reserved for experienced users. - -The Licensee shall be responsible for verifying, by any or all means, -the suitability of the product for its requirements, its good working order, -and for ensuring that it shall not cause damage to either persons or -properties. - -9.2 The Licensor hereby represents, in good faith, that it is entitled -to grant all the rights over the Software (including in particular the -rights set forth in Article 5). - -9.3 The Licensee acknowledges that the Software is supplied "as is" by -the Licensor without any other express or tacit warranty, other than -that provided for in Article 9.2 and, in particular, without any warranty -as to its commercial value, its secured, safe, innovative or relevant -nature. - -Specifically, the Licensor does not warrant that the Software is free -from any error, that it will operate without interruption, that it will -be compatible with the Licensee's own equipment and software -configuration, nor that it will meet the Licensee's requirements. - -9.4 The Licensor does not either expressly or tacitly warrant that the -Software does not infringe any third party intellectual property right -relating to a patent, software or any other property right. Therefore, -the Licensor disclaims any and all liability towards the Licensee -arising out of any or all proceedings for infringement that may be -instituted in respect of the use, modification and redistribution of the -Software. Nevertheless, should such proceedings be instituted against -the Licensee, the Licensor shall provide it with technical and legal -assistance for its defense. Such technical and legal assistance shall be -decided on a case-by-case basis between the relevant Licensor and the -Licensee pursuant to a memorandum of understanding. The Licensor -disclaims any and all liability as regards the Licensee's use of the -name of the Software. No warranty is given as regards the existence of -prior rights over the name of the Software or as regards the existence -of a trademark. - - - Article 10 - TERMINATION - -10.1 In the event of a breach by the Licensee of its obligations -hereunder, the Licensor may automatically terminate this Agreement -thirty (30) days after notice has been sent to the Licensee and has -remained ineffective. - -10.2 A Licensee whose Agreement is terminated shall no longer be -authorized to use, modify or distribute the Software. However, any -licenses that it may have granted prior to termination of the Agreement -shall remain valid subject to their having been granted in compliance -with the terms and conditions hereof. - - - Article 11 - MISCELLANEOUS - - - 11.1 EXCUSABLE EVENTS - -Neither Party shall be liable for any or all delay, or failure to -perform the Agreement, that may be attributable to an event of force -majeure, an act of God or an outside cause, such as defective -functioning or interruptions of the electricity or telecommunications -networks, network paralysis following a virus attack, intervention by -government authorities, natural disasters, water damage, earthquakes, -fire, explosions, strikes and labor unrest, war, etc. - -11.2 Any failure by either Party, on one or more occasions, to invoke -one or more of the provisions hereof, shall under no circumstances be -interpreted as being a waiver by the interested Party of its right to -invoke said provision(s) subsequently. - -11.3 The Agreement cancels and replaces any or all previous agreements, -whether written or oral, between the Parties and having the same -purpose, and constitutes the entirety of the agreement between said -Parties concerning said purpose. No supplement or modification to the -terms and conditions hereof shall be effective as between the Parties -unless it is made in writing and signed by their duly authorized -representatives. - -11.4 In the event that one or more of the provisions hereof were to -conflict with a current or future applicable act or legislative text, -said act or legislative text shall prevail, and the Parties shall make -the necessary amendments so as to comply with said act or legislative -text. All other provisions shall remain effective. Similarly, invalidity -of a provision of the Agreement, for any reason whatsoever, shall not -cause the Agreement as a whole to be invalid. - - - 11.5 LANGUAGE - -The Agreement is drafted in both French and English and both versions -are deemed authentic. - - - Article 12 - NEW VERSIONS OF THE AGREEMENT - -12.1 Any person is authorized to duplicate and distribute copies of this -Agreement. - -12.2 So as to ensure coherence, the wording of this Agreement is -protected and may only be modified by the authors of the License, who -reserve the right to periodically publish updates or new versions of the -Agreement, each with a separate number. These subsequent versions may -address new issues encountered by Free Software. - -12.3 Any Software distributed under a given version of the Agreement -may only be subsequently distributed under the same version of the -Agreement or a subsequent version. - - - Article 13 - GOVERNING LAW AND JURISDICTION - -13.1 The Agreement is governed by French law. The Parties agree to -endeavor to seek an amicable solution to any disagreements or disputes -that may arise during the performance of the Agreement. - -13.2 Failing an amicable solution within two (2) months as from their -occurrence, and unless emergency proceedings are necessary, the -disagreements or disputes shall be referred to the Paris Courts having -jurisdiction, by the more diligent Party. - - -Version 1.0 dated 2006-07-12. diff --git a/deps/CImg/Licence_CeCILL_V2-en.txt b/deps/CImg/Licence_CeCILL_V2-en.txt deleted file mode 100644 index 8720df6..0000000 --- a/deps/CImg/Licence_CeCILL_V2-en.txt +++ /dev/null @@ -1,504 +0,0 @@ - - CeCILL FREE SOFTWARE LICENSE AGREEMENT - - - Notice - -This Agreement is a Free Software license agreement that is the result -of discussions between its authors in order to ensure compliance with -the two main principles guiding its drafting: - - * firstly, compliance with the principles governing the distribution - of Free Software: access to source code, broad rights granted to - users, - * secondly, the election of a governing law, French law, with which - it is conformant, both as regards the law of torts and - intellectual property law, and the protection that it offers to - both authors and holders of the economic rights over software. - -The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[logiciel] L[ibre]) -license are: - -Commissariat à l'Energie Atomique - CEA, a public scientific, technical -and industrial research establishment, having its principal place of -business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. - -Centre National de la Recherche Scientifique - CNRS, a public scientific -and technological research establishment, having its principal place of -business at 3 rue Michel-Ange, 75794 Paris cedex 16, France. - -Institut National de Recherche en Informatique et en Automatique - -INRIA, a public scientific and technological establishment, having its -principal place of business at Domaine de Voluceau, Rocquencourt, BP -105, 78153 Le Chesnay cedex, France. - - - Preamble - -The purpose of this Free Software license agreement is to grant users -the right to modify and redistribute the software governed by this -license within the framework of an open source distribution model. - -The exercising of these rights is conditional upon certain obligations -for users so as to preserve this status for all subsequent redistributions. - -In consideration of access to the source code and the rights to copy, -modify and redistribute granted by the license, users are provided only -with a limited warranty and the software's author, the holder of the -economic rights, and the successive licensors only have limited liability. - -In this respect, the risks associated with loading, using, modifying -and/or developing or reproducing the software by the user are brought to -the user's attention, given its Free Software status, which may make it -complicated to use, with the result that its use is reserved for -developers and experienced professionals having in-depth computer -knowledge. Users are therefore encouraged to load and test the suitability -of the software as regards their requirements in conditions enabling -the security of their systems and/or data to be ensured and, more -generally, to use and operate it in the same conditions of security. -This Agreement may be freely reproduced and published, provided it is not -altered, and that no provisions are either added or removed herefrom. - -This Agreement may apply to any or all software for which the holder of -the economic rights decides to submit the use thereof to its provisions. - - - Article 1 - DEFINITIONS - -For the purpose of this Agreement, when the following expressions -commence with a capital letter, they shall have the following meaning: - -Agreement: means this license agreement, and its possible subsequent -versions and annexes. - -Software: means the software in its Object Code and/or Source Code form -and, where applicable, its documentation, "as is" when the Licensee -accepts the Agreement. - -Initial Software: means the Software in its Source Code and possibly its -Object Code form and, where applicable, its documentation, "as is" when -it is first distributed under the terms and conditions of the Agreement. - -Modified Software: means the Software modified by at least one -Contribution. - -Source Code: means all the Software's instructions and program lines to -which access is required so as to modify the Software. - -Object Code: means the binary files originating from the compilation of -the Source Code. - -Holder: means the holder(s) of the economic rights over the Initial -Software. - -Licensee: means the Software user(s) having accepted the Agreement. - -Contributor: means a Licensee having made at least one Contribution. - -Licensor: means the Holder, or any other individual or legal entity, who -distributes the Software under the Agreement. - -Contribution: means any or all modifications, corrections, translations, -adaptations and/or new functions integrated into the Software by any or -all Contributors, as well as any or all Internal Modules. - -Module: means a set of sources files including their documentation that -enables supplementary functions or services in addition to those offered -by the Software. - -External Module: means any or all Modules, not derived from the -Software, so that this Module and the Software run in separate address -spaces, with one calling the other when they are run. - -Internal Module: means any or all Module, connected to the Software so -that they both execute in the same address space. - -GNU GPL: means the GNU General Public License version 2 or any -subsequent version, as published by the Free Software Foundation Inc. - -Parties: mean both the Licensee and the Licensor. - -These expressions may be used both in singular and plural form. - - - Article 2 - PURPOSE - -The purpose of the Agreement is the grant by the Licensor to the -Licensee of a non-exclusive, transferable and worldwide license for the -Software as set forth in Article 5 hereinafter for the whole term of the -protection granted by the rights over said Software. - - - Article 3 - ACCEPTANCE - -3.1 The Licensee shall be deemed as having accepted the terms and -conditions of this Agreement upon the occurrence of the first of the -following events: - - * (i) loading the Software by any or all means, notably, by - downloading from a remote server, or by loading from a physical - medium; - * (ii) the first time the Licensee exercises any of the rights - granted hereunder. - -3.2 One copy of the Agreement, containing a notice relating to the -characteristics of the Software, to the limited warranty, and to the -fact that its use is restricted to experienced users has been provided -to the Licensee prior to its acceptance as set forth in Article 3.1 -hereinabove, and the Licensee hereby acknowledges that it has read and -understood it. - - - Article 4 - EFFECTIVE DATE AND TERM - - - 4.1 EFFECTIVE DATE - -The Agreement shall become effective on the date when it is accepted by -the Licensee as set forth in Article 3.1. - - - 4.2 TERM - -The Agreement shall remain in force for the entire legal term of -protection of the economic rights over the Software. - - - Article 5 - SCOPE OF RIGHTS GRANTED - -The Licensor hereby grants to the Licensee, who accepts, the following -rights over the Software for any or all use, and for the term of the -Agreement, on the basis of the terms and conditions set forth hereinafter. - -Besides, if the Licensor owns or comes to own one or more patents -protecting all or part of the functions of the Software or of its -components, the Licensor undertakes not to enforce the rights granted by -these patents against successive Licensees using, exploiting or -modifying the Software. If these patents are transferred, the Licensor -undertakes to have the transferees subscribe to the obligations set -forth in this paragraph. - - - 5.1 RIGHT OF USE - -The Licensee is authorized to use the Software, without any limitation -as to its fields of application, with it being hereinafter specified -that this comprises: - - 1. permanent or temporary reproduction of all or part of the Software - by any or all means and in any or all form. - - 2. loading, displaying, running, or storing the Software on any or - all medium. - - 3. entitlement to observe, study or test its operation so as to - determine the ideas and principles behind any or all constituent - elements of said Software. This shall apply when the Licensee - carries out any or all loading, displaying, running, transmission - or storage operation as regards the Software, that it is entitled - to carry out hereunder. - - - 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS - -The right to make Contributions includes the right to translate, adapt, -arrange, or make any or all modifications to the Software, and the right -to reproduce the resulting software. - -The Licensee is authorized to make any or all Contributions to the -Software provided that it includes an explicit notice that it is the -author of said Contribution and indicates the date of the creation thereof. - - - 5.3 RIGHT OF DISTRIBUTION - -In particular, the right of distribution includes the right to publish, -transmit and communicate the Software to the general public on any or -all medium, and by any or all means, and the right to market, either in -consideration of a fee, or free of charge, one or more copies of the -Software by any means. - -The Licensee is further authorized to distribute copies of the modified -or unmodified Software to third parties according to the terms and -conditions set forth hereinafter. - - - 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION - -The Licensee is authorized to distribute true copies of the Software in -Source Code or Object Code form, provided that said distribution -complies with all the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - - 2. a notice relating to the limitation of both the Licensor's - warranty and liability as set forth in Articles 8 and 9, - -and that, in the event that only the Object Code of the Software is -redistributed, the Licensee allows future Licensees unhindered access to -the full Source Code of the Software by indicating how to access it, it -being understood that the additional cost of acquiring the Source Code -shall not exceed the cost of transferring the data. - - - 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE - -When the Licensee makes a Contribution to the Software, the terms and -conditions for the distribution of the resulting Modified Software -become subject to all the provisions of this Agreement. - -The Licensee is authorized to distribute the Modified Software, in -source code or object code form, provided that said distribution -complies with all the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - - 2. a notice relating to the limitation of both the Licensor's - warranty and liability as set forth in Articles 8 and 9, - -and that, in the event that only the Object Code of the Modified -Software is redistributed, the Licensee allows future Licensees -unhindered access to the full source code of the Modified Software by -indicating how to access it, it being understood that the additional -cost of acquiring the source code shall not exceed the cost of -transferring the data. - - - 5.3.3 DISTRIBUTION OF EXTERNAL MODULES - -When the Licensee has developed an External Module, the terms and -conditions of this Agreement do not apply to said External Module, that -may be distributed under a separate license agreement. - - - 5.3.4 COMPATIBILITY WITH THE GNU GPL - -The Licensee can include a code that is subject to the provisions of one -of the versions of the GNU GPL in the Modified or unmodified Software, -and distribute that entire code under the terms of the same version of -the GNU GPL. - -The Licensee can include the Modified or unmodified Software in a code -that is subject to the provisions of one of the versions of the GNU GPL, -and distribute that entire code under the terms of the same version of -the GNU GPL. - - - Article 6 - INTELLECTUAL PROPERTY - - - 6.1 OVER THE INITIAL SOFTWARE - -The Holder owns the economic rights over the Initial Software. Any or -all use of the Initial Software is subject to compliance with the terms -and conditions under which the Holder has elected to distribute its work -and no one shall be entitled to modify the terms and conditions for the -distribution of said Initial Software. - -The Holder undertakes that the Initial Software will remain ruled at -least by the current license, for the duration set forth in Article 4.2. - - - 6.2 OVER THE CONTRIBUTIONS - -A Licensee who develops a Contribution is the owner of the intellectual -property rights over this Contribution as defined by applicable law. - - - 6.3 OVER THE EXTERNAL MODULES - -A Licensee who develops an External Module is the owner of the -intellectual property rights over this External Module as defined by -applicable law and is free to choose the type of agreement that shall -govern its distribution. - - - 6.4 JOINT PROVISIONS - -The Licensee expressly undertakes: - - 1. not to remove, or modify, in any manner, the intellectual property - notices attached to the Software; - - 2. to reproduce said notices, in an identical manner, in the copies - of the Software modified or not. - -The Licensee undertakes not to directly or indirectly infringe the -intellectual property rights of the Holder and/or Contributors on the -Software and to take, where applicable, vis-à-vis its staff, any and all -measures required to ensure respect of said intellectual property rights -of the Holder and/or Contributors. - - - Article 7 - RELATED SERVICES - -7.1 Under no circumstances shall the Agreement oblige the Licensor to -provide technical assistance or maintenance services for the Software. - -However, the Licensor is entitled to offer this type of services. The -terms and conditions of such technical assistance, and/or such -maintenance, shall be set forth in a separate instrument. Only the -Licensor offering said maintenance and/or technical assistance services -shall incur liability therefor. - -7.2 Similarly, any Licensor is entitled to offer to its licensees, under -its sole responsibility, a warranty, that shall only be binding upon -itself, for the redistribution of the Software and/or the Modified -Software, under terms and conditions that it is free to decide. Said -warranty, and the financial terms and conditions of its application, -shall be subject of a separate instrument executed between the Licensor -and the Licensee. - - - Article 8 - LIABILITY - -8.1 Subject to the provisions of Article 8.2, the Licensee shall be -entitled to claim compensation for any direct loss it may have suffered -from the Software as a result of a fault on the part of the relevant -Licensor, subject to providing evidence thereof. - -8.2 The Licensor's liability is limited to the commitments made under -this Agreement and shall not be incurred as a result of in particular: -(i) loss due the Licensee's total or partial failure to fulfill its -obligations, (ii) direct or consequential loss that is suffered by the -Licensee due to the use or performance of the Software, and (iii) more -generally, any consequential loss. In particular the Parties expressly -agree that any or all pecuniary or business loss (i.e. loss of data, -loss of profits, operating loss, loss of customers or orders, -opportunity cost, any disturbance to business activities) or any or all -legal proceedings instituted against the Licensee by a third party, -shall constitute consequential loss and shall not provide entitlement to -any or all compensation from the Licensor. - - - Article 9 - WARRANTY - -9.1 The Licensee acknowledges that the scientific and technical -state-of-the-art when the Software was distributed did not enable all -possible uses to be tested and verified, nor for the presence of -possible defects to be detected. In this respect, the Licensee's -attention has been drawn to the risks associated with loading, using, -modifying and/or developing and reproducing the Software which are -reserved for experienced users. - -The Licensee shall be responsible for verifying, by any or all means, -the suitability of the product for its requirements, its good working order, -and for ensuring that it shall not cause damage to either persons or -properties. - -9.2 The Licensor hereby represents, in good faith, that it is entitled -to grant all the rights over the Software (including in particular the -rights set forth in Article 5). - -9.3 The Licensee acknowledges that the Software is supplied "as is" by -the Licensor without any other express or tacit warranty, other than -that provided for in Article 9.2 and, in particular, without any warranty -as to its commercial value, its secured, safe, innovative or relevant -nature. - -Specifically, the Licensor does not warrant that the Software is free -from any error, that it will operate without interruption, that it will -be compatible with the Licensee's own equipment and software -configuration, nor that it will meet the Licensee's requirements. - -9.4 The Licensor does not either expressly or tacitly warrant that the -Software does not infringe any third party intellectual property right -relating to a patent, software or any other property right. Therefore, -the Licensor disclaims any and all liability towards the Licensee -arising out of any or all proceedings for infringement that may be -instituted in respect of the use, modification and redistribution of the -Software. Nevertheless, should such proceedings be instituted against -the Licensee, the Licensor shall provide it with technical and legal -assistance for its defense. Such technical and legal assistance shall be -decided on a case-by-case basis between the relevant Licensor and the -Licensee pursuant to a memorandum of understanding. The Licensor -disclaims any and all liability as regards the Licensee's use of the -name of the Software. No warranty is given as regards the existence of -prior rights over the name of the Software or as regards the existence -of a trademark. - - - Article 10 - TERMINATION - -10.1 In the event of a breach by the Licensee of its obligations -hereunder, the Licensor may automatically terminate this Agreement -thirty (30) days after notice has been sent to the Licensee and has -remained ineffective. - -10.2 A Licensee whose Agreement is terminated shall no longer be -authorized to use, modify or distribute the Software. However, any -licenses that it may have granted prior to termination of the Agreement -shall remain valid subject to their having been granted in compliance -with the terms and conditions hereof. - - - Article 11 - MISCELLANEOUS - - - 11.1 EXCUSABLE EVENTS - -Neither Party shall be liable for any or all delay, or failure to -perform the Agreement, that may be attributable to an event of force -majeure, an act of God or an outside cause, such as defective -functioning or interruptions of the electricity or telecommunications -networks, network paralysis following a virus attack, intervention by -government authorities, natural disasters, water damage, earthquakes, -fire, explosions, strikes and labor unrest, war, etc. - -11.2 Any failure by either Party, on one or more occasions, to invoke -one or more of the provisions hereof, shall under no circumstances be -interpreted as being a waiver by the interested Party of its right to -invoke said provision(s) subsequently. - -11.3 The Agreement cancels and replaces any or all previous agreements, -whether written or oral, between the Parties and having the same -purpose, and constitutes the entirety of the agreement between said -Parties concerning said purpose. No supplement or modification to the -terms and conditions hereof shall be effective as between the Parties -unless it is made in writing and signed by their duly authorized -representatives. - -11.4 In the event that one or more of the provisions hereof were to -conflict with a current or future applicable act or legislative text, -said act or legislative text shall prevail, and the Parties shall make -the necessary amendments so as to comply with said act or legislative -text. All other provisions shall remain effective. Similarly, invalidity -of a provision of the Agreement, for any reason whatsoever, shall not -cause the Agreement as a whole to be invalid. - - - 11.5 LANGUAGE - -The Agreement is drafted in both French and English and both versions -are deemed authentic. - - - Article 12 - NEW VERSIONS OF THE AGREEMENT - -12.1 Any person is authorized to duplicate and distribute copies of this -Agreement. - -12.2 So as to ensure coherence, the wording of this Agreement is -protected and may only be modified by the authors of the License, who -reserve the right to periodically publish updates or new versions of the -Agreement, each with a separate number. These subsequent versions may -address new issues encountered by Free Software. - -12.3 Any Software distributed under a given version of the Agreement may -only be subsequently distributed under the same version of the Agreement -or a subsequent version, subject to the provisions of Article 5.3.4. - - - Article 13 - GOVERNING LAW AND JURISDICTION - -13.1 The Agreement is governed by French law. The Parties agree to -endeavor to seek an amicable solution to any disagreements or disputes -that may arise during the performance of the Agreement. - -13.2 Failing an amicable solution within two (2) months as from their -occurrence, and unless emergency proceedings are necessary, the -disagreements or disputes shall be referred to the Paris Courts having -jurisdiction, by the more diligent Party. - - -Version 2.0 dated 2006-07-12. diff --git a/deps/CImg/README.txt b/deps/CImg/README.txt deleted file mode 100644 index e2cd347..0000000 --- a/deps/CImg/README.txt +++ /dev/null @@ -1,173 +0,0 @@ --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - ____ _ _ ____ - (_ _)( )_( )( ___) - )( ) _ ( )__) - (__) (_) (_)(____) - ___ ____ __ __ ___ __ ____ ____ ____ __ ____ _ _ - / __)(_ _)( \/ )/ __) ( ) (_ _)( _ \( _ \ /__\ ( _ \( \/ ) - ( (__ _)(_ ) (( (_-. )(__ _)(_ ) _ < ) / /(__)\ ) / \ / - \___)(____)(_/\/\_)\___/ (____)(____)(____/(_)\_)(__)(__)(_)\_) (__) - - - C++ Template Image Processing Toolkit - - ( http://cimg.sourceforge.net ) - - _cimg_version - --------------------------------------------------------------------------------- - -# Summary -#--------- - - The CImg Library is an open-source C++ toolkit for image processing. - It consists in a single header file 'CImg.h' providing a minimal set of C++ - classes and methods that can be used in your own sources, to load/save, - process and display images. Very portable (Unix/X11,Windows, MacOS X, FreeBSD, .. ), - efficient, easy to use, it's a pleasant library for developping image processing - algorithms in C++. - -# Authors and contributors : -#---------------------------- - - - David Tschumperle (project leader) ( http://tschumperle.users.greyc.fr/ ) - - - Maksim Aizenshtein - - Antonio Albiol - - Haz-Edine Assemlal - - Vincent Barra - - Wolf Blecher - - Romain Blei - - Yohan Bentolila - - Jerome Boulanger - - Pierre Buyssens - - Sebastien Coudert - - Frederic Devernay - - Francois-Xavier Dupe - - Gerd von Egidy - - Eric Fausett - - Jean-Marie Favreau - - Sebastien Fourey - - Alexandre Fournier - - Hon-Kwok Fung - - Vincent Garcia - - David Grimbichler - - Jinwei Gu - - Jean-Daniel Guyot - - Matt Hanson - - Sebastien Hanel - - Michael Holroyd - - Christoph Hormann - - Werner Jainek - - Daniel Kondermann - - Pierre Kornprobst - - Orges Leka - - Francois Lauze - - Xie Long - - Thomas Martin - - Cesar Martinez - - Jean Martinot - - Arnold Meijster (Center for High Performance Computing and Visualization, University of Groningen/The Netherlands) - - Nikita Melnichenko - - Julien Morat - - Baptiste Mougel - - Jovana Milutinovich - - Guillaume Nee - - Adam Newgas - - Francisco Oliveira - - Andrea Onofri - - Renaud Peteri - - Martin Petricek - - Paolo Prete - - Adrien Reboisson - - Klaus Schneider - - Jakob Schluttig - - Veronique Souchaud - - Konstantin Spirin - - David G. Starkweather - - Rainer Steffens - - Grzegorz Szwoch - - Thierry Thomas - - Yu-En-Yun - - Vo Duc Khanh - - Phillip Wood - - Bug Zhao - - Haibo Zheng - -# Institution -#------------- - - GREYC Image / CNRS UMR 6072 / FRANCE - - The CImg Library project started in 2000, at the INRIA-Sophia - Antipolis/France ( http://www-sop.inria.fr/ ), in the ROBOTVIS / ODYSSEE Team. - Since October 2004, it is maintained and developed in the Image team of - the GREYC Lab (CNRS, UMR 6072), in Caen/France. - Team web page : http://www.greyc.ensicaen.fr/EquipeImage/ - -# Licenses -#---------- - - The source code of the CImg Library is distributed under - two distinct licenses : - - - The main library file 'CImg.h' is *dual-licensed* : - It can be either distributed under the CeCILL-C or CeCILL license. - (see files 'Licence_CeCILL-C_V1-en.txt' and 'Licence_CeCILL_V2-en.txt'). - Both are Free-Software licenses : - - * CeCILL-C is adapted to the distribution of - library components, and is close in its terms to the well known GNU LGPL license - (the 'CImg.h' file can thus be used in closed-source products under certain - conditions, please read carefully the license file). - - * CeCILL is close to (and even compatible with) the GNU GPL license. - - - Most of the other files are distributed under the CeCiLL license - (file 'Licence_CeCILL_V2-en.txt'). See each file header to see what license applies. - - These two CeCiLL licenses ( http://www.cecill.info/index.en.html ) have been - created under the supervision of the three biggest research institutions on - computer sciences in France : - - - CNRS ( http://www.cnrs.fr/ ) - - CEA ( http://www.cea.fr/ ) - - INRIA ( http://www.inria.fr/ ) - - You have to RESPECT these licenses. More particularly, please carefully read - the license terms before using the CImg library in commercial products. - -# Package structure : -#-------------------- - - The main package directory CImg/ is organized as follows : - - - README.txt : This file. - - Licence_CeCILL-C_V1-en.txt : A copy of the CeCiLL-C license file. - - Licence_CeCILL_V2-en.txt : A copy of the CeCiLL license. - - CImg.h : The single header file that constitutes the library itself. - - examples/ : A directory containing a lot of example programs performing - various things, using the CImg library. - - html/ : A directory containing a copy of the CImg web page in html - format. The reference documentation is generated - automatically with the tool 'doxygen' (http://www.doxygen.org). - - resources/ : A directory containing some resources files for compiling - CImg examples or packages with various C++ compilers and OS. - - plugins/ : A directory containing CImg plug-ins files that can be used to - add specific extra functionalities to the CImg library. - -# Getting started -#----------------- - - If you are new to CImg, you should first try to compile the different examples - provided in the 'examples/' directory, to see what CImg is capable of - (as CImg is a template-based library, no prior compilation of the library is mandatory). - Look at the 'resources/' directory to ease this compilation on different plateforms. - - Then, you can look at the documentation 'html/reference/' to learn more about CImg - functions and classes. Finally, you can participate to the 'Forum' section - of the CImg web page and ask for help if needed. - -# End of file -#------------ diff --git a/deps/CImg/examples/CImg_demo b/deps/CImg/examples/CImg_demo deleted file mode 100755 index 6aba8ce7d386f42f39fc9b18d9e4a303578513ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2067704 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