From 09573d72b51c44daff3dceefa048c627c69c951c Mon Sep 17 00:00:00 2001 From: Oleksii Shliama Date: Thu, 8 Sep 2016 21:53:26 +0300 Subject: [PATCH 01/21] Removed native part. --- ucrop/build.gradle | 4 - .../yalantis/ucrop/task/BitmapCropTask.java | 97 +- .../yalantis/ucrop/view/CropImageView.java | 2 +- ucrop/src/main/jni/Android.mk | 14 - ucrop/src/main/jni/Application.mk | 6 - ucrop/src/main/jni/CImg.h | 56164 ---------------- .../com_yalantis_ucrop_task_BitmapCropTask.h | 22 - ucrop/src/main/jni/uCrop.cpp | 116 - ucrop/src/main/jniLibs/arm64-v8a/libucrop.so | Bin 886640 -> 0 bytes .../src/main/jniLibs/armeabi-v7a/libucrop.so | Bin 525836 -> 0 bytes ucrop/src/main/jniLibs/armeabi/libucrop.so | Bin 554500 -> 0 bytes ucrop/src/main/jniLibs/x86/libucrop.so | Bin 1009152 -> 0 bytes ucrop/src/main/jniLibs/x86_64/libucrop.so | Bin 952528 -> 0 bytes 13 files changed, 57 insertions(+), 56368 deletions(-) delete mode 100644 ucrop/src/main/jni/Android.mk delete mode 100644 ucrop/src/main/jni/Application.mk delete mode 100644 ucrop/src/main/jni/CImg.h delete mode 100644 ucrop/src/main/jni/com_yalantis_ucrop_task_BitmapCropTask.h delete mode 100755 ucrop/src/main/jni/uCrop.cpp delete mode 100755 ucrop/src/main/jniLibs/arm64-v8a/libucrop.so delete mode 100755 ucrop/src/main/jniLibs/armeabi-v7a/libucrop.so delete mode 100755 ucrop/src/main/jniLibs/armeabi/libucrop.so delete mode 100755 ucrop/src/main/jniLibs/x86/libucrop.so delete mode 100755 ucrop/src/main/jniLibs/x86_64/libucrop.so diff --git a/ucrop/build.gradle b/ucrop/build.gradle index 65ae29187..ea8880c77 100644 --- a/ucrop/build.gradle +++ b/ucrop/build.gradle @@ -29,10 +29,6 @@ android { resourcePrefix 'ucrop_' - sourceSets.main { - jni.srcDirs = []; - } - } dependencies { diff --git a/ucrop/src/main/java/com/yalantis/ucrop/task/BitmapCropTask.java b/ucrop/src/main/java/com/yalantis/ucrop/task/BitmapCropTask.java index 6b5f1972e..9ad68035d 100644 --- a/ucrop/src/main/java/com/yalantis/ucrop/task/BitmapCropTask.java +++ b/ucrop/src/main/java/com/yalantis/ucrop/task/BitmapCropTask.java @@ -1,7 +1,8 @@ package com.yalantis.ucrop.task; +import android.content.Context; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import android.graphics.Matrix; import android.graphics.RectF; import android.media.ExifInterface; import android.net.Uri; @@ -14,11 +15,15 @@ import com.yalantis.ucrop.model.CropParameters; import com.yalantis.ucrop.model.ExifInfo; import com.yalantis.ucrop.model.ImageState; +import com.yalantis.ucrop.util.BitmapLoadUtils; import com.yalantis.ucrop.util.FileUtils; import com.yalantis.ucrop.util.ImageHeaderParser; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.OutputStream; +import java.lang.ref.WeakReference; /** * Crops part of image that fills the crop bounds. @@ -31,9 +36,7 @@ public class BitmapCropTask extends AsyncTask { private static final String TAG = "BitmapCropTask"; - static { - System.loadLibrary("ucrop"); - } + private final WeakReference mContext; private Bitmap mViewBitmap; @@ -51,9 +54,11 @@ public class BitmapCropTask extends AsyncTask { private int mCroppedImageWidth, mCroppedImageHeight; - public BitmapCropTask(@Nullable Bitmap viewBitmap, @NonNull ImageState imageState, @NonNull CropParameters cropParameters, + public BitmapCropTask(@NonNull Context context, @Nullable Bitmap viewBitmap, @NonNull ImageState imageState, @NonNull CropParameters cropParameters, @Nullable BitmapCropCallback cropCallback) { + mContext = new WeakReference<>(context); + mViewBitmap = viewBitmap; mCropRect = imageState.getCropRect(); mCurrentImageRect = imageState.getCurrentImageRect(); @@ -84,10 +89,9 @@ protected Throwable doInBackground(Void... params) { return new NullPointerException("CurrentImageRect is empty"); } - float resizeScale = resize(); try { - crop(resizeScale); + crop(); mViewBitmap = null; } catch (Throwable throwable) { return throwable; @@ -96,38 +100,43 @@ protected Throwable doInBackground(Void... params) { return null; } - private float resize() { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(mImageInputPath, options); - - boolean swapSides = mExifInfo.getExifDegrees() == 90 || mExifInfo.getExifDegrees() == 270; - float scaleX = (swapSides ? options.outHeight : options.outWidth) / (float) mViewBitmap.getWidth(); - float scaleY = (swapSides ? options.outWidth : options.outHeight) / (float) mViewBitmap.getHeight(); - - float resizeScale = Math.min(scaleX, scaleY); - - mCurrentScale /= resizeScale; - resizeScale = 1; + private boolean crop() throws IOException { + // Downsize if needed if (mMaxResultImageSizeX > 0 && mMaxResultImageSizeY > 0) { float cropWidth = mCropRect.width() / mCurrentScale; float cropHeight = mCropRect.height() / mCurrentScale; if (cropWidth > mMaxResultImageSizeX || cropHeight > mMaxResultImageSizeY) { - scaleX = mMaxResultImageSizeX / cropWidth; - scaleY = mMaxResultImageSizeY / cropHeight; - resizeScale = Math.min(scaleX, scaleY); + float scaleX = mMaxResultImageSizeX / cropWidth; + float scaleY = mMaxResultImageSizeY / cropHeight; + float resizeScale = Math.min(scaleX, scaleY); + + Bitmap resizedBitmap = Bitmap.createScaledBitmap(mViewBitmap, + Math.round(mViewBitmap.getWidth() * resizeScale), + Math.round(mViewBitmap.getHeight() * resizeScale), false); + if (mViewBitmap != resizedBitmap) { + mViewBitmap.recycle(); + } + mViewBitmap = resizedBitmap; mCurrentScale /= resizeScale; } } - return resizeScale; - } - private boolean crop(float resizeScale) throws IOException { - ExifInterface originalExif = new ExifInterface(mImageInputPath); + // Rotate if needed + if (mCurrentAngle != 0) { + Matrix tempMatrix = new Matrix(); + tempMatrix.setRotate(mCurrentAngle, mViewBitmap.getWidth() / 2, mViewBitmap.getHeight() / 2); + + Bitmap rotatedBitmap = Bitmap.createBitmap(mViewBitmap, 0, 0, mViewBitmap.getWidth(), mViewBitmap.getHeight(), + tempMatrix, true); + if (mViewBitmap != rotatedBitmap) { + mViewBitmap.recycle(); + } + mViewBitmap = rotatedBitmap; + } int top = Math.round((mCropRect.top - mCurrentImageRect.top) / mCurrentScale); int left = Math.round((mCropRect.left - mCurrentImageRect.left) / mCurrentScale); @@ -138,20 +147,34 @@ private boolean crop(float resizeScale) throws IOException { Log.i(TAG, "Should crop: " + shouldCrop); if (shouldCrop) { - boolean cropped = cropCImg(mImageInputPath, mImageOutputPath, - left, top, mCroppedImageWidth, mCroppedImageHeight, mCurrentAngle, resizeScale, - mCompressFormat.ordinal(), mCompressQuality, - mExifInfo.getExifDegrees(), mExifInfo.getExifTranslation()); - if (cropped && mCompressFormat.equals(Bitmap.CompressFormat.JPEG)) { + ExifInterface originalExif = new ExifInterface(mImageInputPath); + saveImage(Bitmap.createBitmap(mViewBitmap, left, top, mCroppedImageWidth, mCroppedImageHeight)); + if (mCompressFormat.equals(Bitmap.CompressFormat.JPEG)) { ImageHeaderParser.copyExif(originalExif, mCroppedImageWidth, mCroppedImageHeight, mImageOutputPath); } - return cropped; + return true; } else { FileUtils.copyFile(mImageInputPath, mImageOutputPath); return false; } } + private void saveImage(@NonNull Bitmap croppedBitmap) throws FileNotFoundException { + Context context = mContext.get(); + if (context == null) { + return; + } + + OutputStream outputStream = null; + try { + outputStream = context.getContentResolver().openOutputStream(Uri.fromFile(new File(mImageOutputPath))); + croppedBitmap.compress(mCompressFormat, mCompressQuality, outputStream); + croppedBitmap.recycle(); + } finally { + BitmapLoadUtils.close(outputStream); + } + } + /** * Check whether an image should be cropped at all or just file can be copied to the destination path. * For each 1000 pixels there is one pixel of error due to matrix calculations etc. @@ -170,14 +193,6 @@ private boolean shouldCrop(int width, int height) { || Math.abs(mCropRect.right - mCurrentImageRect.right) > pixelError; } - @SuppressWarnings("JniMissingFunction") - native public boolean - cropCImg(String inputPath, String outputPath, - int left, int top, int width, int height, - float angle, float resizeScale, - int format, int quality, - int exifDegrees, int exifTranslation) throws IOException, OutOfMemoryError; - @Override protected void onPostExecute(@Nullable Throwable t) { if (mCropCallback != null) { diff --git a/ucrop/src/main/java/com/yalantis/ucrop/view/CropImageView.java b/ucrop/src/main/java/com/yalantis/ucrop/view/CropImageView.java index 09535be30..b5666647a 100644 --- a/ucrop/src/main/java/com/yalantis/ucrop/view/CropImageView.java +++ b/ucrop/src/main/java/com/yalantis/ucrop/view/CropImageView.java @@ -82,7 +82,7 @@ public void cropAndSaveImage(@NonNull Bitmap.CompressFormat compressFormat, int compressFormat, compressQuality, getImageInputPath(), getImageOutputPath(), getExifInfo()); - new BitmapCropTask(getViewBitmap(), imageState, cropParameters, cropCallback).execute(); + new BitmapCropTask(getContext(), getViewBitmap(), imageState, cropParameters, cropCallback).execute(); } /** diff --git a/ucrop/src/main/jni/Android.mk b/ucrop/src/main/jni/Android.mk deleted file mode 100644 index 4a31411a7..000000000 --- a/ucrop/src/main/jni/Android.mk +++ /dev/null @@ -1,14 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := ucrop -LOCAL_SRC_FILES := uCrop.cpp - -LOCAL_LDLIBS := -landroid -llog -lz -LOCAL_STATIC_LIBRARIES := libpng libjpeg_static - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,libpng) -$(call import-module,libjpeg) \ No newline at end of file diff --git a/ucrop/src/main/jni/Application.mk b/ucrop/src/main/jni/Application.mk deleted file mode 100644 index 198bfde4d..000000000 --- a/ucrop/src/main/jni/Application.mk +++ /dev/null @@ -1,6 +0,0 @@ -APP_STL := gnustl_static -APP_ABI := armeabi armeabi-v7a x86 x86_64 arm64-v8a -APP_CPPFLAGS += -frtti -APP_CPPFLAGS += -fexceptions -APP_CPPFLAGS += -DANDROID -APP_PLATFORM := android-14 \ No newline at end of file diff --git a/ucrop/src/main/jni/CImg.h b/ucrop/src/main/jni/CImg.h deleted file mode 100644 index 59bf5e518..000000000 --- a/ucrop/src/main/jni/CImg.h +++ /dev/null @@ -1,56164 +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.eu ) - # - # 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 170 - -/*----------------------------------------------------------- - # - # 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 -#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:4127) -#pragma warning(disable:4311) -#pragma warning(disable:4312) -#pragma warning(disable:4512) -#pragma warning(disable:4571) -#pragma warning(disable:4640) -#pragma warning(disable:4706) -#pragma warning(disable:4710) -#pragma warning(disable:4800) -#pragma warning(disable:4804) -#pragma warning(disable:4820) -#pragma warning(disable:4996) -#define _CRT_SECURE_NO_DEPRECATE 1 -#define _CRT_SECURE_NO_WARNINGS 1 -#define _CRT_NONSTDC_NO_DEPRECATE 1 -#endif - -// Define correct string functions for each compiler and OS. -#if cimg_OS==2 && defined(_MSC_VER) -#define cimg_sscanf std::sscanf -#define cimg_sprintf std::sprintf -#define cimg_snprintf cimg::_snprintf -#define cimg_vsnprintf cimg::_vsnprintf -#else -#include -#if defined(__MACOSX__) || defined(__APPLE__) -#define cimg_sscanf cimg::_sscanf -#define cimg_sprintf cimg::_sprintf -#define cimg_snprintf cimg::_snprintf -#define cimg_vsnprintf cimg::_vsnprintf -#else -#define cimg_sscanf std::sscanf -#define cimg_sprintf std::sprintf -#define cimg_snprintf snprintf -#define cimg_vsnprintf vsnprintf -#endif -#endif - -// Include OS-specific headers. -#if cimg_OS==1 -#include -#include -#include -#include -#include -#include -#elif cimg_OS==2 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#ifndef _WIN32_IE -#define _WIN32_IE 0x0400 -#endif -#include -#include -#include -#endif - -// Look for C++11 features. -#if !defined(cimg_use_cpp11) && __cplusplus>201100 -#define cimg_use_cpp11 1 -#endif -#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 -#include -#include -#endif - -// Configure the 'abort' signal handler (does nothing by default). -// A typical signal handler can be defined in your own source like this: -// Without OpenMP support: #define cimg_test_abort() if (is_abort) throw CImgAbortException("") -// -// or -// -// With OpenMP support: #define cimg_test_abort() if (!omp_get_thread_num() && is_abort) throw CImgAbortException("") -// -// where 'is_abort' is a boolean variable. -#ifndef cimg_test_abort -#define cimg_test_abort() -#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 -#if cimg_OS==2 -#define cimg_verbosity 2 -#else -#define cimg_verbosity 1 -#endif -#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 -#define cimg_display 1 -#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 -#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 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 libcurl support. -// (http://curl.haxx.se/libcurl/) -// -// Define 'cimg_use_curl' to enable libcurl support. -// -// Libcurl may be used to get a native support of file downloading from the network. -// (see method 'cimg::load_network()'.) -#ifdef cimg_use_curl -#include "curl/curl.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) - -// 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() - 1; ptrs>=(img)._data; --ptrs) -#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 stuff 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 - static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; - static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; - static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; - static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; - static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; - static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; - static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; - static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; - static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; - static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; - static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; -#else - static const char t_normal[] = { 0 }; - static 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,15] 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; - if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } - return mode; - } - - // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character - // at the end of the string. -#if cimg_OS==2 && defined(_MSC_VER) - inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { - va_list ap; - va_start(ap,format); - const int result = _vsnprintf(s,size,format,ap); - va_end(ap); - return result; - } - - inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { - int result = -1; - cimg::mutex(6); - if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); - if (result==-1) result = _vscprintf(format,ap); - cimg::mutex(6,0); - return result; - } - - // Mutex-protected version of sscanf, sprintf and snprintf. - // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. -#elif defined(__MACOSX__) || defined(__APPLE__) - inline int _sscanf(const char *const s, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsscanf(s,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _sprintf(char *const s, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsprintf(s,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsnprintf(s,n,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { - cimg::mutex(6); - const int result = std::vsnprintf(s,size,format,ap); - cimg::mutex(6,0); - return result; - } -#endif - - //! 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); - } - - //! Set current \CImg openmp mode. - /** - The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. - \param mode Desired openmp mode. Possible values are: - - \c 0: Never parallelize (quiet mode). - - \c 1: Always parallelize. - - \c 2: Adaptive parallelization mode (default behavior). - **/ - inline unsigned int& _openmp_mode(const unsigned int value, const bool is_set) { - static unsigned int mode = 2; - if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } - return mode; - } - - inline unsigned int& openmp_mode(const unsigned int mode) { - return _openmp_mode(mode,true); - } - - //! Return current \CImg openmp mode. - inline unsigned int& openmp_mode() { - return _openmp_mode(0,false); - } - -#define cimg_openmp_if(cond) if (cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))) - - // Display a simple dialog box, and wait for the user's response. - 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); - - // Evaluate math expression. - 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 (except \b CImgAbortException). - CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. - These classes can be: - - - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. - This is the only \c non-derived exception class. - - - \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 CImg. - } 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, ap2; \ - va_start(ap,format); va_start(ap2,format); \ - int size = cimg_vsnprintf(0,0,format,ap2); \ - if (size++>=0) { \ - delete[] _message; \ - _message = new char[size]; \ - cimg_vsnprintf(_message,size,format,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(); \ - } \ - } \ - va_end(ap); va_end(ap2); \ - - char *_message; - CImgException() { _message = new char[1]; *_message = 0; } - CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } - CImgException(const CImgException& e) { - const int size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - } - ~CImgException() throw() { delete[] _message; } - CImgException& operator=(const CImgException& e) { - const int size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - return *this; - } - //! Return a C-string containing the error message associated to the thrown exception. - const char *what() const throw() { return _message; } - }; - - // The CImgAbortException class is used to throw an exception when - // a computationally-intensive function has been aborted by an external signal. - struct CImgAbortException : public std::exception { - char *_message; - CImgAbortException() { _message = new char[1]; *_message = 0; } - CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } - CImgAbortException(const CImgAbortException& e) { - const int size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - } - ~CImgAbortException() throw() { delete[] _message; } - CImgAbortException& operator=(const CImgAbortException& e) { - const int size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - return *this; - } - //! Return a C-string containing the error message associated to the thrown exception. - const char *what() const throw() { return _message; } - }; - - // 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 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 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 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 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 ~max(); } - static T max() { return (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)-1; } - 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; } - }; - -#if defined(CHAR_MAX) && CHAR_MAX==255 - 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 0; } - static char max() { return (char)-1; } - static char inf() { return max(); } - static 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 char val) { return (unsigned int)val; } - }; -#else - 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 ~max(); } - static char max() { return (char)((unsigned char)-1>>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; } - }; -#endif - - 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 ~max(); } - static signed char max() { return (signed char)((unsigned char)-1>>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 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)-1; } - 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 ~max(); } - static short max() { return (short)((unsigned short)-1>>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)-1; } - 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 ~max(); } - static int max() { return (int)((unsigned int)-1>>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)-1; } - 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 ~max(); } - static long max() { return (long)((unsigned long)-1>>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 -DBL_MAX; } - static double max() { return DBL_MAX; } - static double inf() { -#ifdef INFINITY - return (double)INFINITY; -#else - return max()*max(); -#endif - } - static double nan() { -#ifdef NAN - return (double)NAN; -#else - const double val_nan = -std::sqrt(-1.0); return val_nan; -#endif - } - 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 -FLT_MAX; } - static float max() { return FLT_MAX; } - 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 type { - static const char* string() { static const char *const s = "long double"; return s; } - static bool is_float() { return true; } - static bool is_inf(const long 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 long double val) { -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif - } - static long double min() { return -LDBL_MAX; } - static long double max() { return LDBL_MAX; } - static long double inf() { return max()*max(); } - static long double nan() { const long double val_nan = -std::sqrt(-1.0L); return val_nan; } - static long double cut(const long double val) { return valmax()?max():val; } - static const char* format() { return "%.16g"; } - static double format(const long double 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 { - unsigned int nb_wins; - pthread_t *events_thread; - pthread_cond_t wait_event; - pthread_mutex_t wait_event_mutex; - CImgDisplay **wins; - 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) { -#ifdef __FreeBSD__ - XInitThreads(); -#endif - wins = new CImgDisplay*[1024]; - 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() { - delete[] wins; - /* - 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 -#define cimg_lock_display() cimg::mutex(15) -#define cimg_unlock_display() cimg::mutex(15,0) - -#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). - static 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
rv$5v|@O^$GYy-!~(dA{nE0( zIXLZaR;-TlFA+0sDQgln%p1#?HyoTUTYDVuTgjT&QWvKugVV#oY4E#1XI2WQC)@o` zq|w((6HZSCr-y^n6ToSu38yE6)5F2(3E;HSgwvD3>EYn?1aMku!s*H2^l)%`0ywQS z;q+v1dN?>e0i0HvaC$O0Jsg~#08T4SI6WDh9u7`V0H>8EoSqC$4+p0wfYVA7PEQ7> zhlA4-z-gte0+T0$)5F2(3E;HSdXRP{IGqDdj|ZnkAK`QvI9+IQx?eMUDofYh?pt&w zGD9-I{b|1|7nF3)Z<+Ah7WiY2%OAw)ub(^HSzluR^&rjnS7B?GY$9HhJaWN1j5qNo z&q+qy#xp+Fq_)CSA^1t_QkCqZoJ+i(aM+E{1TRVVXyP}-nsfMazhX{&+~ab8X#}3y zj_hIJDaF+=^mUN2?}MkjIIxl*6bH7Fb*wqGU-2x$E`Kawon$!AOn^V;KrhYX^QDu( zA8GK%9OgCg$9(A@%;RbBhtk9!^QCLRA8GK1(!?M0rB}coY4C^A#2@peL%<(t@Q2dG zAM?={a^a6O_(N&pkNL!h&4oYG;18vVKjw!>gFn*X52cAe<_{wc{z!vAlqUX|KY}#) zBMtsgn)qY>@ua~YY4C^A#2@odA`SjXgFloe{;0?g7l=Rn@Q3Il{+K%!eFpwe9G@s_ zz~;@?(y{Q!A7qmSld4^O9p~~$(<$)C6D}r!O;>{jCS_$cvmUp2{O5m6pOIw-Glrl}C1RRzWI{ zoUODJ9$B@Uv=kocp|lhpneuPaQh4M=&KpnVk&#ME;gQ>jt(3|mJ3neYh)2#QO*|t0 z5T9sWSbQox< z4lUf5|=UGRHGb=qq zaySq*WI6xsBwk;HShLbK+S0+QkxD1ttn`hxbnt2lXIMvwIV+u`Egj5S#rf3{;?7F% zXiEpTcGi(j>{;m^ZRudwNTm~hR{BRvn!U(&N`-I_KBRh?{UI+3FrETj6_#%}?v$;#aLfZQhzHTlKTP z?%7@Nt7I!r2hPdy*EPDbjASe6z?+kF;G%4YUwdRb{Q3^MwW9;4rRc!H#4n>Jc57p@ zY!$Gw)x0kuTZx{}!Jjqor(~-aTpf62k`BB8*=jtzIT@ZD$9$@}a{;o|c=*!MfgRau z0kYM2c+$~<9ocFDvekI_(b0h&*=hl@)p&T((SaS=Y5}s<_%(CytxxBO`SK-R$9uuDQr!hH+v{8MYfs(rl!uDFM_A3^X5p> zQe~^#z}Mt?bCuTD(N_!Id2!>d}n6_m?M}1MUz4XO*Tz%2{oOKtb z1*+znfVJyMU)-FeFP5b_{5LYqUpmR!_4>K`VlYKt>@3#1|0~COm;C!Im^CJCU3-7wwIHWLDBL~9YXAUrZGRViATilWA+ zOcGEt)u*%uB`(bXYNgVO+FEOE4d6nGR;ehA3VFZ3v&@`1OahAS|9#*8lh0>9nRDG| z?&Z3#{a!BL67GnpO0fS&{^LA+M@t76j-J3ih!8q>IC5^DO9wCJevS}2csQ~y1xE*O zd=)r4csTMd1xE+x362gPj?7EJ(ZLh`4jdgk9J!Z*qk|X!4LCY@II=GVM+a|w1vol* zIPxzAM+fH#jt(A<3{1h%!4v)p934CyIhcZ@gBQOH934CyS(t*OgE#&KI68PZ@-PKQ z2j>Zn4jzt7Ou^B?6J7$24wg?Ld^$RK@t=Xabg=fpxn<%>4!u5~onB{i&q;Um>clsm zA?E1NtK!fDs5g^d6^G7`pY5Sn=yd=1##P;Z0)sUb{tnsF={f+AZ7JH&G1@I(AE#eOg|- zWg~mDymm{8U>>_=F?+PUc1s8Qvb=W71i?IZOEdekymm{TU>>`rPI}X0w`@d5dhM1H z!8~@$V)UlhZs|ZrdhM18(wiQ;r5U}6UNY^L=S`iI_VLHtG#0xplg4DHNSEBoy#)v3 z%e_u~)*Z9ogRaZw%YCg28j~JL*~WhmUv|AdRJ6&Wv5sdf8avqap?`-jXH3lQ%S|{J zpCpSfw|D~ZEWX^vbAV^@%4q8+aC9Zt-~FS$w&T%4K3p|T2 zx405`7GG{-1@J7sT%O=re7Onbz_a*ri$lO&dc-jAH%-&)UH`IVVr*V^dVP~SB)Zch zj!Y~w={4=kUG{2rnRx%$gOG{8O3TD-zTEm>+A{H%7QOx_eYwP4`^&@g$Fpn=cK`@Um*Ss8!KRHIvv!|FbcMF@1Zvuj)4THY;|1C-GLtj*(7&4EnpuUZ+X?&NtFsf3`Jr z?JwW5q_ZW_BU4Vgb@74+EV`TUfJJw2*f9j%gROP++>|apxI3HW13%frS|>MdH0f%V zVpz^PdEE`2S|^{a_>~nSU=90vVBGIEHWeda4O_52z)~@SoE?jQ46HA(RE&T%>_x!x zfu&*utYHf_2v{mckh7ziJIC)C1}qgLU=3TaATaD>XPvxm0kGk~QZWMhHy_vuz)~>+ z`X|^)z)~>+`WFFqDzH?Hfd0+v)H?ZW_d59mU=5=~vulV$?Y8Z?8k?@B@3iPD^j*vU z(_Rc`ABOUZuH_uOd)~DUsJ#^W&4vwx0XLnCm-05oV z?G=_+*f{&dxR6}b1%Ri0oTx~iKA%tKcjuLS0ys}jLHbhUT}Fb`dI{1BLjt|kcP zp{wRAfO+UDPcRQ%)%^gNhpskW4$MPWCBVFNwfM3$UDbH}gg?D&(Uoydc6n6Dcns)=wV#9TO06dEgS0Z>88?Nqs;8|?A=3T(E*l->10ncK?l?a~2hO7G*aF-2t z%kIorK#5JSkyA72)rnnQ!~GWB*>FxQ;Avv7jt*5U;Go9c} zd)d0#>(Fqi9SgYhKNAah(#DyiJ6?Im;_>it;4I7jkdg!7%&~jtxNs)B?N# zyk4G(vuyFcZMCv{yN>s5sqvX>`OP)H|Kst#2!2Smc;80+kZke3Ji*~{Z@g~;{zk-Z;PALN-j^r1i^m@$M}*>i4!xdg(`#g8c6$9U?l?IZ9)EUm zCXWxH-fZ!{i5_~*7VjJTrvvc#xi*iV`=8KyD_?a7JR zxtXQ!6LxN9#fY6fIgz*aWvm-1N6gk8`!d#zs)1Dq=2|xrtnnYfJnKezf_c`B>NUaZ~XTQY+!943m&94J~@k|p~u z3LRMX*Phh9?5`=cX{Pl5yzGx1on7{K9EA)cM(17YDG@x2?62z!9Q)3@*3*0>@GP>w z;|SncWPgd^S!93R;lQ)V{^mZwv&jCA-oUfS{u05n$o{&+fM=2Y&3VAH$o`IAz_ZBy z62V=v|I2$a?Ym#SZOO4*p@UC-M5Z9rz<&KX-y)9zVAkKg8?j<_YHUbLE$K{oIZCBwjzaL@*sdhhj{(m3Ba8A$-Sl=9LBzCok!~z54>#Kz&nkRz_=@XAbW9q-1Fn(o*!T7 zCD2uBUpMRLKiQS3Uwhkh+5V1^Rlk0R-ye*h9KRvcPktQQ%jPHNk2$cPJpUuvB$+Wh zx1YQqX_&E=2}#3VPa}5sAM}$I^K<;<*uw07a>oMvJ>;n3eq+%6Y*#S%t(6Fl998Tu z1!v8?Za#42sN#PqIBVw35#Y#C#Q;-q*33KR0Y{E{_pOx(jvV#wTdTVccosjo`M-c? z@sm5|0!NN|_pOx(jvQ5NFx4N{%V}_P;A*6ZG-^ROux$Hr~N(A%RgNyG3=CKDm?f~Yp z2ld+tg1PKL!J3x?bJ>G}%d03wIp(IOyVF?7>+kZKT)0egSP{`c{@bxZ}$QmSetW z4Rc?B`%IsEEu!f6mK^KY*&vC+#zRvGt~|%{bGS z2aK3<#+kmlb--8ybFWowOaNmI%)M4oA{c96?zM`=ZNOLqbFWo&{010nVD7bw34*Z( z=3c94ejFHUVD7bwJi%B4bFWp@JqC<5F!x%;#z%p%2IgL?C=rY`F!x%;;zvwfn{lSE zqt(>4=DvVU?`P6j$fmKx%Iq|DGj~ZGjIMn3$C&g}Q(xz@jS5}-2+}4#T zIJ$Ddc;L3KOu^Y-wtgINTUVyw>@V{RZtKbvoc(1LV}aYcG6iRU*@8;owysRU*4jT21T9VShfbt_)n%y{?ShW9iBX_Z*t8j7`~JSLOlp>dLwr zU>;q$F$~P3D@z3P=*q>_z&yILSJqt!%%dweUI5Ia zD@z3P=*q>D_t%vj-_6vOoBowa7f0B15xOOlE}XUe3l4%V{%2994R!mA2d0b4Z+1@? zv0E*=sJqpni-V7;jUl$=#MIiqu|Hi*0OqBO=C1?u&_$kL9=fRe8ZZxCZ2T%P4_%Z9 z=AnzlUjgQ!i;nYvdFWz-U>>??o(RlC7kPqt=%Nm}>ZOZ~$W-rM>=IyZ8*02w7d5uL zxUs{M7l|vf`+|SsPMm|Wp^i6cBW*)XhBmUCz0O}dXKZg-m??XHM2-aa9+9ke z(iir%EZ~f7BxFQaFo)WQ@0_va-iFf94Hg~cK}SLJjBQRvJU;-B`0aSUZ1V!-jT6ro zO)T{LmqZM|c~0p1OQ(z;$2&T|rZYi0V>=E1O|gJnj1~F8Iwv#@*(19;7yn-{ow1#U z?2%oai#;Ql&e%>v_Q7QuAJb{eurc6BcHieNfp zI}M#AyE+$JMKGPQojQX1x7ZJ#<2ola1^>;@9-w8MM?M}~MRz7e_U&!?{(q6%`<=9{ z{oh~eTt1R#?vpX^y88S-Rn|WqeYbO<6SqIYq=B@I`7U&ImAzLi{GgF_ub5k|towEM za%Jn)7CkgyZON7Y=>EiS?k`tLfO+N0;woSsxzceyFppfBAecw4G*1HNkt=zEdE`o+ zbg4(KY(z(Tw?hj90Jzxa2G@4jm*YxzS??_55acVFZV&~nT`J$IZe9?;&zUV5>j6B8roB97LGP%gSSHvIH9uCo~)}jsF|?U&;T*6Kam|pHTDF2+u*DYk2nY96F(p=fbasbLXGA z?ISCuwDO4b8QFWUow7&!Umbf$x(`}Sy6qwRjBIioy3cb)HaQO6=Q$&r9Ea}noRLjt zIU}3Qaz-|p<&11{+;C_AYwC<_avZwPb4E5f4&CQDBbywD?(>|HO^!qNS@%Sx&d4UE z`=+^cA95?H{jY{GbVu;Xy;Ti!e9MkE^_sPhzkPJ)^0;el<`tfwvG+pApMQzxYh8OI z{>bwPJ3iGw`DUJd_V4w)iRW+FwwHeYG|#``eeJu}-DPLZPs>d4#Bnbi*neBdJtYmE zdrI0|_mo7&+J605$A9C_H*;MEKMtD6p&#AdQ?k-^Psu%=J1GA5o|4RaO4e`>Ma-YS zBk}9KRYyQeT)>)&hku2+e#t%~QVz~1@O*~rc^uCbZ>3`b4y`;G z$)uI~=MPLP#oSlY9j$~z7Oj+oJ~^#i&D|SL-){2s?W%+5TO~PlyX#wUyw$gkai6?z zUv=FJvIAW)4x80E@BDY3e``KlcM)#mxy?RjQNujF%JX=89>v3d<@sV;MyUMHJily~ zTlanaj_02C`|~M(mggV1Vt~)^{2RMHmH(FKMdm(fYyJ2Uo@d(o6jXm3&-3i(a-JXM zxx}^S@?oAQxn$CVJpbOcFXxv$54HEasl8wDTxZ+$dcK$ER(m{pZsPf6n=Wg3UcvJX z)GuAGyFdc0nY#6P4f=eYNw@vuZT#1J8_@Yp9-aSpv&{i<_J?x!Y~=a*mxf1|UlJTG z86>|=c7g73=|Nw*E8hohoi^P4(C?qL+t_e`Hr}R=?%H_WZli&5G^ECnWA^cz2l(xa zuKsy(@ZWa->iORtJ}XUEeU=v|kpti&-c5gJ*!{i2?r*kvy~KX! zBKw^LG@`TC^HX>W>=2E7-+u2x^MC*Nh;*O#qm}dRy09JhcyZXD^V)ZDN2%^HUHXsi z=W(VzwyyBk&&J`32@y_p|BV>PKh$mR-sGZt(YC@+!G3*1x!~D#&lAkl(^E20TN2Xh{zF zH44bDF`xVzvcsMH8pqlBH5Qy|^o@}}gZs3X(1) zFa8ETTFIH6%L{XjrA2(?a~0b6mETF6Np<-pbgy-ulC$`qH|~Q z=0fB6XkXg!k(bxc$1Jzoh|q>bSN-G1(#|U#X*w(9S+@5@zws@j{&#hcyY6jQpI(`j z8sBEdx0>;hAA|8x<{qE?zn#R*(`~26S3}zqWq+v7ZfO5X^St|jcs_jM(0tLzPfqh& z2Yt3QZ+xM9n)jayO(n9>+$M|W{GX>hmZkiDyZjW&BU#FCvdd4VJe;NcGQ0dF%0pSo z&$G)H6bMV|q+~SkUmd3+BTRcZbcaP@FoN=MmfzcWUT_-ap)BP#o)>(P^5Fh>{;6Wh{-I`$q(;s?_PnWjwEWXv#K3cj zzxNsIJdz4?8<>UoUu8%Qy=<~B`clWsgg^Qwa7)usm-SBb8@G~PEF-beVQ14 zT1KUFr;eWMTeb+<=gpn^GVMq<7Ci1-ws+3s&3gmf&q>_qz4Lv|@Aos3TZ!47AF19` zmEUhkHFs^3!>CI7;MN7kb2Z5EUC91*+=E}k+F?8Q(bupRa1M1g_6Q~Q{!HFmhklI| z8PC1nPxY3Q%VfiSV}9hz*LT76tQ?KG$n?E)er@()m2$LB&mjj&pm`N{8Sm!K)knx@ zP{dwa-4(Ah%If1sGjp4D5B^8=$rsT++#&JzFYq_7y240~MbGK|)>=Pz=?{+kdZmB+ zWxMRLZ7Lg|k=d5lbw~b<$jpSS@44%Xqmi7S}Wz~EH+rymeKeA<#&xjly z9$>VFkKl6{pB{YtMr+M6e1`Bj+Gt$>KBYtR(G!c>PmGpda@FV{`5ctLp}>>7B68`} z(E-Y8DVyiX^)SXTrnfT}(qSjsw4(klke&>k6s;iNK?D1kGH4+-K3TE!?Bp87EqbsX zH_F)Q`?#~}A%54wXAPgVd|s?LHM-0{GPB)B1d8#p!k8^%dK)h`5Tvx)JFC#Num zCUP0IV+*bSLg(@(a;!wiw;X0aeI&H!^e}6QA^qq6T|@ubQ=b^a|D!{#;j#Q55NZwT z{}BFH@c$UzKXcFN%GnY-!^)du=s$UL!e25*7|=(<72M4NZ5Yr5IM`80%*&*s3o4+M zO7i85g(k*9V}CGdN_}(ID1J{LV`ks`$EO@^L^s;c!dqcvWOPcA5q-i9O91;KHY6~; zqu&-9qoPx$7|}<74P-6_Gk+gE86F-OZ*l)V0*rQk>Hd8?FxrU&Di$mlxPhp~ygCagV%kgr|k12O*So6*3!Bj-oDerK(3Mj^CY!&;>1)EUo0 z`c@b@B^sem%A*@3_lWZ6#)vD2$iXDKyZIVRMrjP9yQb;C1g!mIW+FhXHr2v6El~~BKl8G7C-&(@|~M}Gv2(c)viy2|xZ$Xp}_k-wF(ZzJDv z5jm0?z*jBuq9Vum#zo*+^#sXLrFw>g6P+hm1n;k~dB4cP%S!4Z$Fbl~A7fOd;CJ|D z#8}H#A9N1Eoe!s&-UB2@exZrNi9Pr-d zJIo(<+E?X(_b%Vr?3=NLb#;>l!D)9mR4$*$Ke>|!`C+`(dAjib68>a>u}ons?(x<- z;{{jqcPnRNH}ABAlMi@@v2XXt6&f%<$S{Bxi_GuQL!a{$|{%W*MwHZ;+FbI=g%aSy=u$?=XK|z5^W;zQ#MuUzhLP&D?C` z9dOa*JM?GE7T(c*v#$CKzSgz#j`p8*^$v8v-j+#;S39ddf`-NG)xZk3cUH{?C+0J} z+CW?92uHko2k#a0f2SQoadMlPWg2$EMc4>4GwR3Bw(XW1sNc!6_GGG!{_zs( zecWzi`&FqvFGVM?^@FsBl#qK5`G)Ugp!Te+Xi{*(aDd8a`+g=<7>&orn%liOy&Ih)3uF>lGxmBt7&enWiaN94s?4*qv1H4ISpJGVFXSn;=cSKHOTPHi;5SxJe?RIRX$(Z}Pc}x#9w}!H+;=3; z=+WR@W9W9?SFX<*`Xe3tdvuEI&%xNJvNs0u{{hD7<9*S8fZuKfM^3)$?{dz#hB4{4 z4`2()R^7&XJ?-D$%pNSwi)`8PM&KFI>=t;Iu_p`2MQTj#Q9nLEG}`=rVNU&6BQVi7 z%vXO8`5si4>Mk&HruV@Y!heZx%`uL8llUZi*rxa4jEMS*9z1F{cDZc(XFlw#(pZXm z8b_7&wBFl<-caA)o#|VqdBG+d@y9vLKlG$rYT8#9L2nyje^lRtd>0*bz|&tPUyacZ zyL_jG$KV%@k-~)`9Fi;E*fZ~i<={|$l!LKfnS>1ll5Y~%ZN7hPWtzR^R^ zYtDsl&Gl3lzH6rX>SK#SAJ;1OexXrs7_*Nl1n0p^j(6eQqU$X-O@p85CwXXEIPa5% zru%?%#ZEo@7eo@=O_Uc*l0y#S} zV*JGai?CgS3FWu;#RIAMf1or(jBZ}QzoUp8H^Eqza$q$x?okUH&HJ;Vk8s z+2z3`mmWvLL@zCL}>t0w+EZi+aAK+eR(Sw@~ z(|o@_9lrRt{ppY#dqWH1`@&pfjU!*vd_N?E@BKF4ORkFVD_wLLnhM{K4XxGO8yEJh z_YLyZA8YfyC0i}NN57kVFS+XQy(3rMeD9X4;`_L1{|t%0f-aZ6BAzXL+UD6)_Q%zE z9$bCph5d2W9nYShMMgTfT6f-Oz_VjRkEMCG>@(-t?}IPl$k&Ff_2`jYaI@R3M@~1Y zSkGN8TB@i#EZRuSsED~1T~!zZpJ^n=w)D!|Bhg*dH4L3JZ0@k3k$It8W7s>p%8eH3 zpBVN|gqVx&3sG*;p<`m?XEyRzxb_gX_vmlNXUi$C&KW>_mVCZN_3R-W7SB7GxkWFo zvG)*u>oj9cS9;2zY=3$k7(a}9)mP~$H@((nqgV8lNw1gW+4NfKvIQ)9t$<$7we{3Q zTV|H0bdoW>^yKU~k-cl;|3MG_;p0`r1KoUl`ET~8&F=Vk)5fm(xcr1{c=>-1A1g<^ z@KPT4;01kA?ZL|fYxc)WcRc$xxgWctm&CBofM@^BdUgi5aofy8J@`2Jf&KB}*vzt( zWi!kEbZq9`Pj@bNY`Ly%X04OUmOIY2nPp?RZRX=#HuG?7X8$lhHgnN_wgvI5xMN>P zw)f6rUu?=^U&v-Y&8CTlJ3TZ3ue?kQ)6FY~ui766-O@%Q=f3qBa zi+upQax#60*i!VK?L&n4E+68hQ`n=XJp=fGN6zTp4>VW_NTi$J6xVarB_qPrHAka5gZ0{SST1 z{z;!>tc&R03;F9OGLBlsSFm@tpocoyGqQeDXI0~Ap`>N=`VVOHzSh^WdE4zhFNbRL zCa`lHo43=yzsw+f?1vHky-x~<*_W~_uJ)9QXa`tev@7P1m)o@<(Jvz z8z>KDDL>CHUr%{(f19_j@C?1yG7q8^*$?Q3ohA5Etc7m2*Hf2nfF?CJ&YEg%Xry@` z^&8CLRi}wg$?t8?c?ENRC-MZkTq?SJnCBlc#}}R0x!j?(1FZyb0G8&EQ)@ln>qenFe{^0RfW z%_uA2)7ELU_M^T$?7ev(lCwS#EG?W{aQ->0M;7{w5e>W_Jm<90Gl`)n9(oG%QE-AW zqXFNpkl(4k0?PG&K5+@HGmN>(m=Q*gG|`^q(#l6!zl!;zjqe4b>r1V5ubZFsRXqsY zltaeR(e>1|g)y~lGg^OmQs;8kLq|~du3(07W^@YWmjaiKC>+Oluc0&4sI`HZ*A&b-n#P0 z^44+qeXHgM_~Z(yDEa8N+-TbzU+Wt78RXBM^FrG^>Rae*ebAr3 zr0oX27xyS@d&Wpc2KbSq{o=Qs4P9J%)!P22#>`lhM|M$q%T($9Vdw&gE12FW9y~PHE-7&Xy^hvG8$DwZJ~7 zI?t6as+_&)5iKH@#zN=q$R@4Ljp@ZRdt<_SX6<84AD-DG(@qRiYi?uu z^2}bDu%21d7?aO4`)1l%@6|q=F@tz!KTTNAtW}H|#xwhD+KB<>ur4ts$TRzI!g>z! zJe=ns<@hx@!+Ab|XU?RC^?U-)C-KaA)OP48=Oms_<(adnVLhM9Gcg*@r1Cb&VK0a|wJNE4H7}vp49uX{=R#Mku+EXUVq*pS5yh ziw_HtX`0)@PG42q2IIN+&L2xGejKuIJo^XEM(&-%{(;J7WM@?SQq*qfrOpvLH#;BO zLUH)l*sJp~b1nUE^5m^XtML)l%%MpC?MgaXmnOuoaLv5WZ+f5vM050A#GjpV`b z>Bl9Rd@6oyfQK4cI~IR7k@I_HZ78{_I%LXB@v4_kZ&4lM(?9yU<Yx($jUkmTYRj>Si?KNwF4!3r$cs_v)+4^6xeSqIM>mGC@qN97WKm2xNlbA^LeGs=K-6~D{Vdx*nD0NpZnnR$$^181_cH# z83BIQ5zE#*ZnzxYX6?!3ZFt4a+fNho%)^c`^gjE>ebb#YBLmIz7;jM)BL;8n;~5yA z3{U?|Fk2UfUhO<2U0Celu}`B5A3DM2v0r!3W1m(R8q36g{o>D)v+7WF;X3MY>%!b8 zKbbD{9d7BujqHW#N*9*EGud=uHD_XDuKSq(R~Kg2g}X27UKf5Dy`$KQ?jn5%GAqy*GI7 z;QChdo^U9gCmm;??_`gTA?D!YyKK_JTj!^2(xX_Hy#~E`Eq2j`kNH|g@wpZ|^gUt= zrp`oH9-Mw#ZtAyhfD5mFbKAHJIUDLn*ST$6NAHz$zGX`vi?5rz;p;k+ujL;Yrj6^| zvo5|)*nB;PwKRX=|6Ut+5PH8%bFOtTgxS^bP{P-S?^_o*-Zf?CXH>WJt-T37cU0u`d?xl*XO-FywO7v)jpd~FALNvV*qhME-i1)2dKPCR;<>4^ z+|tM<_69&VjqF_r#j=#&Z7J3bW3QhuIYj;sp>t7m2O zo4pCXH#*lSW}-R$9CIo^V9KoUtSOf|et>mWHsY3V#A%*H=4HsYd50$73MHEkQNFbt zYst5hFZ%b$x5&koeA`SOsY8`-|Ghp_zMX}$1Qr{vr44Eg5kR=yQLr!}^GGhFgbaeKFX<1E6?;MuMPV%k6CEwbSZ-useD@48(BHzl9Z-u6OLvMXr`L@T`w~#p}j_HzbzIf1~iE=d9Zp`M!%77hDW~12b(gh z2KnarXNDQ)kbH|d_8V(ZH)C%_PO$B_h1hT3v3)RNiL&#|_@ZKsiZ3dzsQ4mrMKivL z|8X$!#n*;s$~@Bt!`^Bnu4u*=i7l=qwz!J;i@64nCBFD0)j@pm?JeEQydMW$K3HWs zzIZ1-SLkn&YlGu=kvmI%)(GN@LE?*Fvi+~JwdUF2C+zY ze6e5su7Ki;{ml5HL9Eb;FMbYwde6**+b{lSVvUM1-u??;%UnKP#u$58F~)M*tsD`0}*}!EY_A_zdR9ii;kOsS@o?ggV*>rmnC=Sj-A>NWAojRn$8t^?1GJ+2tmc4XH);?+ULd3&VeyiZF}y*0`W`Xqq>9< zm8=Tj-;ObQ*!Ls)qGNR@BA@x(cUaDShg0l(5FLJ#|EpXYl3lV71HE#VRM_^RF>C7D zYV5k+y4BGZ# zn7cQtu@8f`eHcEKXXJj+whzN2c*Z^q+V)|XyH~T>hd+TIy#8<9#ogG4*e}22o-xHO z3#g3imvqb6^8Z5 zgVm#{I5K+F6-Qp{i6ggq;>fLI(4($6a;qnf+&Tt5>WU+`dg92fo;Y%=Cyw0ei6gg; zL5~K~ah6t39J$pKM{bq>hUHa4*G>SsUP7 zn{QL~+QMFm$$It~M(aZEP1Jrg?h#DIKrcdv+A+|z-Wcd}Zv6@kEQ0@y;&hCl{l$IO z9waM9u=%ry5&Sin$l44qK5u8A6YE*#KBtY`e_yMC;0zq z?>-N5_xX0teQR%(>2n{E;dAei>~i_sNssJGdSqA9BfF9w*_HIjuB1nHB|Wk$>5*MY zkL*f%WLMH7yOJK+mGsE2q_elm?Q?(4mKFHidk*AtPnp`a&;2y_P~68o2_)sXX|Y zqwj|zZ{(+c5_x0F8T=7b?jS$3Rw{Y(CT9)Y^5#uqaK|BUjz!)S+44r~sFu9xwPU?4 zZ?^Hj+?F>h|5x6ad$!rj`5%`z>Qn2mfP|(Rz^kTAJVwx4hZHJ{Ilq z)!mNPS%|?0+}AQnbs%qU{_pPP&A9$9`{onG{fs-%&BQ$?areu?#1yW=-ze(hEX0wI zWyTa%^Q^dE_$XuK6k623v=Cf zyS?t42M;Umr}bdP{Vv94D1lek!NX&Gz8%VkQj0#3jga6|xVGV|wR|f07<{G@{~Jpj zu*ek$jFA7tzy_#AzBuc|mH01OC+1#*s?e;dk7=FQ&)wuxhy}*DdyH7%<60{gtcZA^ zAsD$$nJWuMOi*VqhzUNfHDmKV;(}TmQBKnW{23EQY%nMo`AoHTY{G~SMpz>uKKQuS zk4>0jgsh7YBYa$I$R><9VMs7?m}(u_gb^!@u|7hq@NsC*g5l>Ff|0va>&fPO#0+%? zgP7stT2nS*#0?X~+=&}LUVy)2!iXIP1tVXn)|L&%tN3ArH4@^7kDJd~)|h`Je)F8e z_|BBBeV-e-U*hNFH*waO6+=|~&{<;^{WvkiEpK;1Lq6^e_3c>jHfQwkeGGgbJ+l^L z`aWTNpTt{f-{*Y#qu63-R*jj5ARS*c%sb|F-p$NQZ2De1i4Rztp5{k6Vc)%l7!z@kYy;d_Ik@bN{ga z3v3Uc@o%v`w*9ZKld-<|Nqn8-q2sRB7v<|*PTn22uk$4OTegSJdWAXD`B)FmALZEh zYO$9$fgCl@#PdDOcbzfqhmT=!zIY;S?SfD4;e43Rm-&rQEB7R}a*i+AgR}MXISVxn z`@}p4ANx{t75AV$!rqQ8)W4OF1-I_z{exl+)a5?o_B`^$ea7t%dTttS z&pp;}Ro_(l^bX^>h<2Z9?-PBM@3NtEX89+K^?9Dvj_%Xbf1R;0+bten7W}w#Wb7Hv zL$hD}PhsD(zfq=f9%YZyJ4T(0i{%P}?iPo%8#` zaGCkr1>kZaxLgD-k0MSD&Q^mnaGAc_dcKXbYsfF8wdEJ+ujWJV6wznl<6;+XJ_qb6 z-T^mDg&V#9u06(==!<^a*~8e`fxe$=pA)a)-C928T$%T74~*AhpS}(qyg{4)AP$%A z8}}X9c?l-dx0|T@UfR@H6V#uy`_>8lsNS>L%Tq*M`i^fushvCW*mIFA3>(~$H#okl zpV8_E{@zfdHK6~+Ml0t_C%t!~(aN1ylN1})T?@4t_!%58JoD^i#mD25Wj21)_rKHr zL-dDnt=9SR-|=0zSO045?`rdpyr;Vq)aP4moUF3x`<_5~>%E-yb<=qRXMSUx{hh|W zf6)5URp50?Z|=NiZ1*iBz8O3-`udSZQf2J{qg6bh{%mJ#yIIH2z+KGZh2A!O%skZg z*?Zqz`+x4D{T;+hP67uf+IaYe-H&YaALL#l(Z9xPjS(8K>A$Uu`d^^_Nz|Rg=V(3- zJ&JFI_mPJ>ZfyGQ#(ifO{d$0Y{nNfXA-}sZBO|*S^MWqwzMZ-?wtdwWpM7eYI>4Mx z>7xB>XrDdR10UkEhL3~uXTkY9_L%={_f0aO4&R_#8NfMyM+S5d8_S?$TLyfWHtz+G zCjZ-Y>r87WayZ5R)T8gC;pa^L&yT-n^Zy>3|M%GZzsJk}UK!-@KkZLvY~u3==trIX zUHtbl{Slw<0p`dA$;SI_Tr4}(d15bqcQm+%{Q(&`vw6P{eZJ7{!%Vvm+4#=rhVQm? zQTO}AB}P&ATt3eHJM#ZK|K{;N)zp#romeYn~V;HTSBXw@6erV_NC++r<-?wB!ws~&nUi7ZoySR(?<_M=l zIHylvvE^2qccfy!8TQiXTq=W>9sZ{u&Gr}*-HefS!Y;>nN*80SpzhyMw?oHw!@I(<`nZj@R`KkV=^R;- zkHhDi_^$ER+kN_X$>Pooy9B*x^LRh{j!kObqw_j+@DuR=b;feLYdpH=ZnBNL8QtJ6 z@x88b_i7jIzek)y9&g1rjv?2|0plA(jTYhQvshbc_#QII zUdz1z`cr)4cFwrW<@0Ic8&`pgj5D_b|Ie=FwzH2!cUCmBk3?&^&f1E(&)Kz>+riq3 zvyQ7ZmSb7VJ%P2A<5^odj>I;1>dLgeJtM#`MzE;-O?K3yj#9n3_Ji_`#x)2d9Jm; zdK>roX|1=2{nhJO>uu*A4aIpA*VcZlIBza@5o^6yao%7~Xs_bD0brGOoHy7DnD(m( zX2*GheSi_?HDPv~H`o`L_O0kWJI))-2S%LNgxPW4;2>bcc}!79V;}Fh*NSV{gW|le`?BOi|M|jh z)^4~L_;cj2bFbZ8jh?Nbjbk`pDVf>>K6Cu&I*$(B&yS9!ZRkO)0cF#nH{zeZNS&(t z9s1_@(5E0X%hAdB(3^{>N8b;{hyIz>{!(HLOXUYUKJ+ED|9QrS?p!Lr`45a$diF2$ z={C+>Svq%cT;rBKeG4%4^$GeaJY=(JjI{r>d7NWc*goSIEnD91M_Ol&x8n%^W7m7I zI$QO-eDclrx%?-q?M$Ei=q}nnj`mkEwu|_Tr%uIB6q7%n=Ndjv{OpxN)8<<0*j(A- zXO+;5a5Tpr%e_u~;TM@Pw2O%;C`R%oZQ&!mo5EGlXU2%$wEV99aHY7?we)E=AGK3s zw`2Ns!%e?#weA2E&1Bkj#CBpmQg+=;;xy1?x_;ZP+h+T86W#S%w0=9FR`pa45Nd|p%XusW#7rlm$sN9>c;Ug+$^cp^*Vn&J~DQ4u3 z7yUV-ee!JB{-S)utH7C?)|2Ge6At^@W6eg-!BHt2Y(ILwu8TSPnT^8_FW66Bq;R;9 z@uqEV=7zIzj?LXCV+`rI+g$4X9eq`ww^6qvFN(n7eCjjh1@#DjhaxYo&uBj_FD|70 zVh;{~0uEQ%efkZ3a^`g}eDGP$YfVO<((`({jq4RQuCvZz+MnKU4iE2Q4hyL_#@HtD z3G;E}g5p?`i%S{vdB80h*+2d`?Mp`9?RxiUZ2WKVEZSC#dJA?{694?mz%++fcGKr@ z&#vjN-SWxOyleX&yYOvQ?}c`~pIVN(Vw>6IXrhbu|494iGq$_0cWIgfN311hdK_I50h;iKkZ^1jW!++`!~1$gD%=%K>HiO!+JiO z`3M(pPVPqMrsD|v(f2pIXrqocTJ5?wcT;z)8-0FO7j;jv@%M;b@29521I*tsU9|s2 z+MmhTuH>_pk3(N;p|2L#^D{h8=2`f?r5hY{$BTP2Y^$_QbCivP*@udQ1MuSCx@dnV z{{Igd+bq|3zh?7d4RrlE;1(|qj{lTCN_HLRdRM&IkLN-@``y30A;7(XzPJzD+qr{R zck6zLJ;5ESt=lIWd)sa?T6=|p$F#9dUwOsZPP;k{-rH*JD0bTKWXiXQ0v1@ zq1K1)<;>;0*7_}hfu)bK-u8ZA0Oz88_5J7eum4lPXnB;+eDdf-$Y-#SdsY{i_pCP5 z&o}Q`4RAM#zrKzA$Th5iDpyN_HWyTo(>Ko;AsxLXFt~JUU{EQ0r^wMdp!7KMhDIn) z9A=D&P{$Txe!7eCBi^m)-?{v7?r&6ignK|$9x{xUwcqtETR{$0y|aQmsw;Z%A&=^c z3Zo2oGDe;=y)XTx_wVO@t>uQ$UH!>b+dscQXRQ10h~WFQEHIu^{qIwkyNwn6CQOdn zWt8h(^B&wHNLQ@Z#X&YWj0GV-_y%1P6D3 zi{;?tcJT8X>hTvFd@I-3 zhy3OmXkYW9IguPl%?the3BT1n`tzZg2JV!GURoRG5W@oZqMZirhQ1rRs)w#(I%7%L-&NNg^k014xzP9A-Z|;q zZOYN6Tx}n~!;%5(kn!)sySw0k#YnJklE3_iC?1 zfQTNrs-S+g8wMX!z(Xx~mZ9gJZ8-P(tG|VbK=cIs@$?*|?eRNY{o6@<+N+;{Kj;74w`>o1QC$tk8_7}hLH%kt zo}ACTXUX0C_^BqmU;Jv`|D*j~v8|QsI+yP;p9hv6IiPcSTcz=wX!yPJE4b6QlDQkp zUB2bb%xhG4`KsOdw7bDo$K`F@oyPM&X-{+X8sE3^*=oL%ACJ+mSNR^JPv4|{)uH!% z#7%x>zH|IBmb@JhuQ)O?Y2m)k0y}(>P-A}wWu(Tb% z)f}w-j+Hy7D>{i-bFf-;0?s$DNzur_IQ|4VdwWHTsdrtD@$Py)T4!|P?WZ#b>Wg5n z(2mymEWi5j=-aNo`u22I%`wL{u=EkTUq3rQziis*x?jPb(Y>s9X6x5c^vmgoXhVHm zNc+m4>hyOt?OXhrW8Y_Q@u>19{)KUVDE_o_Ee4U_;#nu3;zt8l3=tp4ZZOO|;zQZr z!CEeZPMa1v1y)|gGZ>rN?rP2xLyQp) zejY9*FEsP6xtFX=QYbu8ICTU;qSI!ER`D%T=6JHILHLyO> z=J!os_oYzT6X%A?UP7)F28Z?D@cmHPqM*_Hl}VwprzQgn4eFhkLY>#qUNF%6$!kMp zcZY`cerZ9dEF3a=KXFm0tTE{Cz2SP^A0I0F4(}V`oZc~PBBM6Yn>lMuJVHI;9=&4= zxl6yE=ih|Np8OVdUPC{x43#N|!QHjFy%Xy-27m7udgsNbuqg%_RabLQKKItN7Q~Dh zefpEvvL|=Y>wiD~7a)J;4I0Wh{-el|Fx^-^e~52xpkD)aS?rcbYa@0Q_t3WnK92m3 zJK`o5<`_qZKaM=Jjk$`E;~-@CdN<6ZPsGy9yq?^5zsB#I*!HZ!o#p+fx4mGr_TPNl zkhVXAtJ*+W!5Cvk8~1_VkIkewSp)WJ5L`8&tl4!|Cfz4obRV|+Gah+j$;yH8W08}R@3C!_mOV#ZA?&%)-$vHf z+Azi}*lB`ov+X{3POwvem6^60ci`T@zT<#(ulu^O2guF1Fvrvd@RTX9PDIYIwlwJh z{>Q#1JHubvKz>c`(JQ@1g~eL?o97rZ!%cV8@eX~>PVvxU3u&G$mZ(R;A5_Tg{%V#YT# zSEgQN-exdoqSprQwVL1CI66{b(dicQhpz~TUhf_f>=Wr7gI=qsQ@``!XH-zf6n?Ac z#8I8g(F>)LS$g(Ub^&E)@cvwEkh`gWC41OoOCqhy$+xz4W~BAwiZh=X0*exb)u}9sY9p!P-{Sh_Pvt@kYk|+G3vKPsBUzqQ&4{(S7@JoWDRW_CP zrt#ZC@;R=5zIt>4`AQ1==GGUf9JtEYZ*|}UO}N3@lhs~e`lgRNtH_J`+>8D}_)ur+M*yy=LxF`?$>epWq9-Y9K zy51NtvFMUxN2|P!`fBWP_u_ti(OP*%=bnARgTaCIejCnRG$|b1Zdi2I z&V8H0!Q0GZHXJ54z*;D%5(D^_&^h$ zmOcLIZzIpSb7;mo*O>q68tW5(J@8mJF?W_MvvNyotde;elQWhL!sij>k?HHJSO1H% z$j%KZ8JSyemNPEZv%!`Ng?$H_aCiT`vNQCeMbkHT!PkBdO>geoxm*jBEZ?^_}nf{`wW3jb2OAfpI#m$Am!=iWN`>#v+DrtYQ5B|AZ=0`SL{l5I4*?IAg z_h-tZM*4jT{dU(8{)0XC*Rt2K!Ba<+ItEimHr`+BdG~tW9gRM4@boM2q5g@G*?Z!!cS4)kcaObeZ0c;;UFBP*9INsPPdfmf+li+*woFz$vu^rx z*1a|ZRLAspug># zJ<_~4pj>0VX8AQAok+}%Jg)8dVGY>LvVCMPCz$v5FYx1|2ke|i?STE9SFjWK!Ml40CQ3)AK)TEGsl^x_lul+Y)STVhS_bSvyv}_$?zf-Zl1k8^}!;428;yh%*&)7Gij>Q0uXM$f=1u zO!QBWId&R$@#l;dJC?Ru{-MSVeIyGLM@7}YVHO>v$8Xbt;wx(Nj)Q13oS$y9p*AlH z&6{m)Ay$=YFWA>?&!9cU+=QcPKOvtzxQJEyQ@P`~k9O#j#N}g*QTB8V?@akAat-_} zCy$!O{xH8?TkMav^1Cnb&M5E@1P>z_|A4s<)wlQuEV-4>HGH@XpHse(aN?d}DN@+3)@nD=435_@_@KCiFgZ;_ipeam5(J$;`k09}9h$^UOW+!iA5y zEhIj!zQCiK8=tL67LtRHHj_FlIiEAOZ6-gJ6Vs}IUnh$vV0UMg-yr`$Un8o%`ZrgPE;jm4Trhy|?bV~F=>NObqu29a<8a2nUU72_ z+7s`s^Kk4!v(7$toz#=6le$!=yRKi@bvIacF$s5jNk^&j#rH_76LkuW{A$eZCveL;L%kE%%-8TW0!lyjM;;itp@U zja{+t{=Bz}I%43w0bDggM@{h4N@!pezh}*0Bx_(xpF)O+pAxKxh<_ws!+hR^r;KpF zXc@NN66)gJ%}+6=_xXMS@4Ckk+-dnC8b`v+gETaLJ-_45AL)tJ269>b*<$&EOAUO% zV#SHTb>8;{6n54W5wHVGH&VElkT))Wew1ljc7caEj( zQE>SlzY8+naA#-Bh5Rnbb7vqk_O0L65bv%w0@Jl`B(!wlkR0NVdaejA98#TQd|@lT zXaTg=&i@eitA@efI6m$;tQmg|_@X7ORj+`jUSGaqi1r3Z-ZTs}l5=QZV+bSHry$pB zk?Zc*c*8*Ei=48KY}fcDYck|pcmVPyKYk-IVDXS8-_!QQ9%2P*FMNQuh)Ji~fp(Cc z88*x6!uI^=6||vT8x9S>;`%PUIy!QpIUoJvPv2q9$CLDH@0|Ng-M9+=mfrg|c4}j- zKdSp?8(AY+12~3mT}JEQ`U5RbaK<6=$zU?Q3D&*N1YE%4<)Z= zPX0hWnxofxc${so8Sbr@yGO6QmE`tAA81|d@?XqT3<>wvFAC7SI#)ar!`59K-?7gS%P1M~1Y&rQt z%>J?_xQ1BRchK`%k80UQ4!*M`zax{j(@#JDAJcvtzEAyG=klTOKx=IdIr~D%?b`20 zx!!rud}na{0^Z5xohc?Q3^2!C(LMZLExrGm z&gDMNWv;ny&I=}=82y)wSN=un)%|%=U7MQNZI|`3%lBcjVm*NJr7L!P;pZuHUzwJzY)(Z@`0}#)OU)%d|e~F9k%yoU%w9R#Xtu0qrYyAKEA-)%N#~y|DeEZYh z;aO?uG$(uQzTXX7Tp{cWOI(WvBYtqFIY`^BsE?4scd zc-9*DnB}z5G&ZBoD)YCI<>3w;ToUt6M|Q!Gd|H2vU8;P71`Nx5irFg z^}fdSIOEfItxM|vi}wGQ?EjLj&e$8UbFbC7`51QjcK$p1OL|FqNO8>&I!x;|;zhSV zr(Ds}QOJ>2`EzZ*WPO3$(=l|`_S1~L+VfJwngMpxX8Be%8wN)oZ1P8MMt1A}eEKWB z7DIPQ_c=OIw59U%&<84)j2A7t_1-wUY%Q|gtM|^L%+Y(;5=*7`WP3^XX)VOjeGe}Q zneE(;{yPG_C!DF>TI7zU0|%IEsg@38&n-Go<;T-+Pw8TL|qJ+Hhx_f8p8i z=t1c|M-L|O8}!>Tyl3h`<(DNEE7+aTg62|yA+6Chh4zeNjK8mqicKfyP_olQSLt$NF6uonQ)pq+Iv`uU+cIXRct}@L0H?(em&wto0z_Zfjs*>6=$s zI&rtD6Itgc?l_-3Y-;z*%;PoSA)#>|ZjOI-1GdU?eme*F81$lWHEQYdAp?XD^kSs< z{(A9;j72s_NIqjRGTPLOmWuz)j&)*o6g>TT4=AMw`Id$K^?M1YnV^=sxED0e1)N5(WYPG!{MLl9*H-g`z`Ro zI@(%q-WP!#AG7Wic%K-DUN!ZM_<^`R(^#Gvl;J zamHfA1ba*xN81J1kkx!@`CQDWDYvZcQ6qWZ2bLe9_SEjYDHi>4hs7lA6I(!k+;P2; zXHYH~vm3qkDE|5G&X(1*DZHq?Ui7H}8WXL}mp-)V>|Wkc{dKqbmNoKw$?o;GzJwlI z8j$lp=bZ-RsdU~J;yvbGG-yk8iy!ilJ^9$+1=!aS&??MNXX{IkBd4}y!1?+v~n|M5I!t&!^ z1LoKl;`NCS8q^GD*kW}D!`>^ry&frszWN6GGxZ8sPn zceZ>7nD&b4eZ>WYJMqi2z+dNCwvWnuAIdK2A1|d3zXvYfypeY_7a6?Sjt}DE%@z2P zX})Cbo^d<(r5pios_x&|?W!!1>Vx&}1GKThRj$~Q`l#P%?BJ?Ob!cC1199hb7{B6J zvfnP}dpYpEa~?4LaQWcce@a5PmvRw+%*RoYKSH57{mKkcpTZ;F$Ve%;}V~^5g+B}p0rhmTh ztmxg9#VP;%;o4AAJp2al3I}H*`@6DDeDqE03z8l4`Hk%HkMk^;#WtGgc67qF=95_lwl^==sEOmxNkhTyN{X z!N#aR0aw{y*ee;7p36titsk#gY_hPw(efg2=}Ds>dn$oH)Px@o-lexBJ7lLkLjN~{ zck$1RpCZ>}&lKas(GRyhlQ7@uAHRfmYxo%;EnoV?*Ia}3TrNyclgqV%tkv5Dt=vQN5@@$bcL z8GozgI}Hd(#t%x#_}^S<*(h!q{~hjV65jGfBkH?krTUJHsG^^&qnR=uyp^tD>;_{A z67Murw|t&j*01E(vPUPDqvy1rUc~$2uZNa9e!BX?x~_CSYs%Sl{@yt+n0~rs%1qi9 zZ96i>Pn(7912@;ybLJ37~hm)$Xh(gO zZ^}AHD>AQjOfNot_=Joy{wJ^DUEy4QEBM%4VHi;(cy{t7+AXr{d<-0%V}9=+Z{>R> z-&Hqb*ldK5$@DV-t+QWZwlOGFR!mz(y^PtxK}Oji-lZ_Ec}L!&2iRjH+F|~!=YH{( zdUnx4jA!9;(XG~bJjKjaht?4_&&jW3>JD?BW1q{I=f3!1={ZiX-!U8J89(uFSnHP1 zeh?pc6?H0pA-!Or51sa8KZ+)XKwIj2HGL93ALrTe^|$i<^L8J_b4T+01kXEqVRO)q z>azU*{_#3wrZnwO|385#u};OE!zt$A9_QcF|v`5`gksT z>I|I$Y4Wk|*{^iO51E70vfDGaBB#O?vS<34_DtC3_wRtS81~Fe%4PfLY=rEYNrG=H zH|ICO?-KBR12%}pt9@}zSFnyU6P{!Zb6$yPfp?JwyBVAL&e`mFU`$%mYGBPX#rH#z z*9EfiH<~0+fWLc=4<%<1H%QZ7dTn6~aTeJ;!vBCTTK3p8KXYLD@%hv*JL-F)8*q}cRq2o9 z>&NF=^65AHrgMXz^9OUPHJ(9;u)Zx-|LBj)NF=IJWt=xXMt5Ai{@6G47B@3!*pJj&+r&OF|o z$8Y8_cfgWu8=SrUV;S!_)W|u)BAdsv zwiFy}j65EBr#%Dcvv&(k*;f?Z-P=HC8p*=oS!Vp`gMLQquAxTj`^84J@pUHns-aKXOB~LEn;O=k^m_-V72q=g z4QW2ScyzzlhxZ=hJ?}ej0~7vO2j_g&VD|M6;e1vua;9rs_8!IAd1%*}58*R4C-nJd z#`O{XuK}kz>r(@dHo$xAziZXLXyIIY@g!I0g8M`tW8`VzcV@-#XzaS{UNGhk;XK)3 zK7;BXL)U5Fg66;o6-Akccbn$GhnxXg3E%4s&?@AB^2^;np z^ZoX-@H2&RN&lQr{9q>MpJemsw<^1UGOfcpW%a2td{4?$_np*buDi0vJ(}|Wuga$| zyy@_g-=|cM8 zl~4KL>yyZ*A>i|1tKH|FrVy!;HB} z^Ymt78pm`qmM?z7v2-V&YCfZ}WYSrBEdOoCfR46h$c;-a`E-Q+yvTl*4Ehq|>PkMH zMp;+#=_JY|pJq^3hJ0Gs1N-S=Gq$t+_#T(bj{e8zdtNSEJI2`?e4?d_`6#}lIFGr% zqFQS{*oFgr$+s$vy`AXE`Ae<*39;hb)Lyk!O}XYeRE)KP_X4@`Ro^y}zmPwLFQ~q_ z{lQf>JXYKzbM5MU;*OLxy+90U1G@1+&a4uj z#WltVJ*%xk?j3v^Snw(rEPPyaJ22x)7c6#MbQ`b;_C(_n^1@SpjJ7LsjBjiNUO{~^ z^z1QCKwjRs#OQo@ z89Fg!3tj3oOf7J;P66ku1!rQrG8eI9?Yfx4D+F`sHGXKjD> zv5_B>HW&RV^9I3XuyP`c$=;IcV|3;La|rT%uEU6|tx(zOK| z<)}!D(--M8O#NERWpCKcx&j$;A8oAG6=z{o?IzCVMGMvVL5uRIQ>Wd^9;}q}zR}*{ zW^B?T*HVAc32F4hlPeXrxdYp7LU##$MHK$*@(tJ19?rMq2VGB#&MiOcdYV2e%@5`u zll5$X^{uBsZTVKA+A`RqCJY6R;xB6i85>=?UMuKD1HF7n>{{{Mq>1`R_#!lO|7eGJEZeiFxNeO<3nIk8@Wi@X1q2d(AN@u%-h` zz76&qxG(+K&>W3DhoR=4!%fRAUE=4oJ;*vK3a*7Nu7@rpkLVgDt+dHsi2V=YVfyMv z>5Nmu@W$B33G1pckgFz`cBVXa>7SkDk=C6Wb-_;*wA1oB->>scebM(sTK4e0hi}@T zvtDfZBj11Io3`=Y*>c3_+*Xs}Z9PIcv@PFI-WJLgTSKX*n>13-3#m^NZGOt7CTwMI z#zWhy`3rqXp9y_AMp!f?d*xD}I?}g1uS^;&Agr9f=uUL%CiMw&t}_U|RC7+EK29&E z6Q(#Pdn$UVgI;3jZvHRyQU|WtFGxklIsi(f{2=hTV%di1X(2eBr zDO;Rw2AOoT%Pw>?=zmE!6HU7L{PV6RDMJHqv^z;RF9J)x{}Z}tpTf8&Pfxn}E8+J+ zGqQgsbRy@R)zUca(4P_(I3ItW#JAA5t~%vH|+oO&wc^>)HP=0yhSq@KgT) z+r_k>l!fE?%Nh4$I0DF=$&Scz~ z;|!WijJ2-07S7&c4^`mFSV$cQ?FYNYZy@YcZFq$|SHKTdIkt~_uyPRV-M6XVr#8;# zL#uy%XUQJ1X`BJxMW6KvaT1sD@NygvScpc>K1sV5Z7oKPvJKj}t?qB!{I0*?K zQ2Yl4*<)n9wm>68C}%r)*T(U4AMN=OK7xh*1WrD*){ag_`f3n;b^CvTr4{Z}&@p|%AFgAnGgMtq7u?v14oBFyKx@K@dy9VT| z!L>%LtE#~6JLV&eX*q$N{=i5rOUDFZ>CeVXcpF z4IaS#vyp2l|Eq8>bbMyKU6+!sK92iTy!r9H((*ibRN7Q*kBUwF(+@rS;AvY|QyVaZ ze%UwQokzQ5uD^^kYku&p)DV-Fv4i}zQpT?*a_`bW{_xOWYuqWNxCaW|3RRU^ly`1)=H-_Hf5g_{iN;Qin9)9vwoGPq00 z?AL$OPfH{{ID)PsMv?5TUE~dqQsZU6V&E=I&wOVMX=*O=21iQ#h2Gc**?VI@U!L^S z*1_ebZLB4KGH0{rRzBQ1PjYC~Mf2aS5PwGHtdS?Cv=6_VAoh=U!;@tURK0GEft}UX7%=0f z8=WhOm$6`ee}}nEz8Mqd_xFv?+sym}+p6!x_F44(oTa&tdF@%+p7<{3o3l%1*`hmd zshMKh5OEH8g7BLl>#Dmi($>iO=?Qqlw!ymT6Kh+x9@*BPzmtEq*7*WyTC#l+;j=AH zEz&aB7iqC+kudODfMc#r;bqVE_eI>;=uKpu`xbgyncI#s{^L3)_G6tPp+W9%()c=XMe8Wwj zzzJNw559k&g4=ia{te#`(7$49mB;rV_zu%ogL&U2@AP91?>pt4J|DpQ4&G(nx}LH= zrQFncYop9t!PArTR$|T)d-LvgV>=SIYydyLL|Y^LAW{FH0&@=EE8}4zUr3zj3}*2z z0~FtZK762{cHz$$3wR6YYAz3@Nt8Mv18|EbTwd&uKQ=38T~xx0M^-bwiX zS3U!8qV1xy{xg05bv^?{|7q=K3~ZCLVdsLEU|csam-_rCJ_FAn?^pQ@d=j5}QlBT{ zG~3hn;$@U2^mS#i#gqF&$LmzL&%kS-pa-*mb>`%l;QW1INQ#55MI z6n|tgM#cY_?CTheP4P=6{>PBxI>on`_$3p)rS_28;#P)Nvuo2l*mso8R14-J)1$u{ zz}k3vepp+uopnZCy5V(`#te(6@sP&MvrAjhB5N-Ec70u%I`;JI@b~qVX`(v%nYj5Xl#p%$MLPlpjeE1s9cdga2w=+~#`>3^fk_MT`Tg^FFbdC2YrS06h4jU%s zOtA&jdH#mE09}Ju?BT_48#=264s?7IN0`3go)8$G;FwS~X%{ml{KM6Ry`=vRbmxV> zu)$n?v~^6#i>>5CDK?n5LyIyW{T(@GBV$nPFEzL24}dcP*nti*&xW*goj`68-%&%_ zm?w9Am-cXOa0_!q@*Jcexix76#(R`J?cy&XZf`l1GWJ1p<$4UZ}fiO#l@_e-VD z<{7UcCcgeo-UPmWO#iRdha49BEB`QTZ>Zx|%8l?BKb9q&KNy5e8N=o>d^Wb2@i5V+ zN?7bnHIXsKx{vwHaD`@5kMJ09od^rM@z=zDM4yzfusd`WVVq%*bi2&F;jy9b5EeTp zB`i2LR4HLYQ}dn`x{NR*H7t5oXbNFr^nA&3V`qgb2ov33GK{;_zCoCu^C-!3V`D>J z3Hye*Hv;ZxM>Ib)Uf_*O$r~IOI!oYLVOE`E=Y)zS@7R>QYFuapVNt6c7GAis(oL8< zwQbRJLegi(l$5%VcaY>2T20_DIxaMTu;963_mK;YSZ7sjDws7KdPJwRTkd&m`mn1> z>}ERYKhBhIy&GB(+KV0N+NzPxvKQo>FKNYRQ*ofHX&im)rw^l?FBSi!d$ZJnHKg?t zzUQ7bd7E_=abtr^-%XUVWiH#y-UxbE_I0q;TFQE{LcNjdQ?s-7LhNoXvV3dW>>)W* zDfIXhcFZ?Jcbd`$6i|l9h6y|9810jFs7Z6!#e{ca%XOaW^fp;Gz+sn~_*|SOJ=D$p z1zNq+)WqPn$);U!3wKH+!lSur;&Q^-zcTHyed^3m%XzY&ligN>F6nLR^5TgTW_gcMJ(FrEW4`hIZXJ?bT7kOtb zQ6=f`Va$mQBzps_zp;J97oOFB*=@IAizjky8L-6;()|f?G~cn0yS6N==stsa2J!cz z^BYEA%J{q!y-d_RlR2c#&O9V?c2qaK`<%f_H|OvFh8(ONQ`VeeR}20L56g!)hk0s? zhGmtl!WXoJ!HY!~o&Q5^0kl>X$)>YSjNyWj7o znc(|!-ZZ;y4s~*y=S{QQ_VRumc|`9RKGwD6QrhyoeABnB!cS8A)^})fr$IkP^R2#3 z^q)ar2cNb2RXe-8UxUNwSHhG18vAm;UJ>utaVhFHYa$g!TTus%7jZj zO*{42+3aCvx19&P7Q&>Re~ZKZQag5G7fU?pTm9`T^Ms1c%N0?w~5a(^9^YeeEm`Kws(!MW6TPTOC8Z+p3-)CGI_(~ zk^0jQANu09%bRGY+zF9Nt4rU(hprix)An8BgN!4Q4@4&T0{=?|jBPF0ecoa`b}GU* z4DhRBV?yA0m+Wy@!!y#D|Lk#F=L@OZBy61xY@PkYT?cH&)Jb!77~fw>`r`d3^yQzZ zi?q>%A1*O%s6|(!GR1!nd^S^gU+>X|4D?M+X4sIny97_>I2+QI1|F_Y>C=^Q90yHX zb*=3<-vKio-x1(b@O&#_t>8^zw_XSR#Wv7?c0P&-C}#G8)N+6)$K*npQ>)7sGHnRn@R_>nddGd44uOh zqR)8)x_yxE^+GMg*E@X$A!kr_+y=!3^SPj2{Tf|{NqB!gvmL}WV#^p zKVjmNAQ=`O7jhHkccg?x$AyLx7G+*Yri(HE6Xs40Q{zJe2n(VEN~Uv<5A`7|#<_}Q zm@z(-L6||9Cm~Zs$A(Nk(my54KQ?q69Ed!Y%*aX31!l z97A2+C(N+YS-4UoL;oa&;uvPd%ItMj5`qtm+@=XCuZ>x8+_zcXEAEbGP?2a!yoA&#@NvHgoU$HWK-yM9$`jSN?6c~UW|13JMkl%_<1JFicSb! zEpXi_VQOONyAswnCCoiBR3&gz!;FccD+F%pI>J9ObO~X~3fpDUt#MxHLc;v1bQ_!) zG6)mBQL--KiJ|idbEk$yCx#{x7G&%u)5Ru+&LJ$u*iDA1lF(?vjMOl9NoW*dVa9GU zolz1hAWWr(`Ab5l5$0z+C({K>Lb-%Rt+8wMOSmMIO_)11ELsxkM_7=v6Un@>5{`dS zKYZ&X!&v*-34=zu!`!8zF8E`Zvl+>BMrr6X!c=OQzclm-VSfCTC({K>Lmv_*V>}ra zE)5+fjJljO#&zb^#MV>bHG)o&i%$q&uzLh4upG>Py9q4N6Aa5!^ z-_2Or-6KrKM$xPISS4KavF{ny{$)A5tbo7AC<*z{lh-iEGcKP%c6dT$n1s&vX7n|Z zZ!h^oha>qs!O@}5(KU3Z?>^U2OL|!+C(ba5p5l7Oz0lwEl~$a<7aM;^Ir3JSB}0~R z)(9L{U6M?b)24`*`vo@e1h>?SaRZto5-K#<*WVn95Co z7aJS8R?=DH-b$y&h2{u6?iuZ_%h*sgVSZ~ITj~5`Lq8CBPN5rXJv-zVT}^k<{;T5s zD(!B_P;0J?_vd2zMb@(A^oy)rJ;Ad=AHtWr`=(oeZ%_73;{4g*HV1uU`UwA*buQV? zcLo}%OV8Rh(oZ=r>&c$aX6-JRGW$eBXB(}<+?HzMimS4UZIWxzw_dmJhD2YB!_{Ej(INiDENf8l5SeSi(MM(0{>fMMRk(L|U%A1B;6wJ!K1NRr-^6}%g1N8u z{gd$0eNOax6EEHC;9o+omS6Smu-~%Y5F4RXUL|}<-`Uk<`9J7R`>liQU-n2FexR$V ztVdYJLtRbh6812EX@3B}jm`14|K$s-?Z5hWZHGT6==79r4~{q6KI|*nel~3vyl|Hi#7(fy+&O=jekE7Pb+$;MEr}y%XmxV75{iD{yV;8<0k1B^Bg30 z*zcG1nlH4Aoc}~W_O4pLqdL#xxehyN2lm+><}R_-7F+JmSj%u0u8lSIYMaJhGxL?7 zc_`J!``r@9{9`sd=ikzcKfzXH-RrD{Imf?cb9v+oTUznz&rURLuCawWIEUDd4KVb& zTX=ZQdS9nsmAxhV5mwxgw(tz>fP2J?-c9leAK3O+Y&-Yz@8Exs|2pnYzz+F~PHcN^ zgPcLzK&OZOaK*o6r;3Ds%h@+Cd)UvGoA-4EnIi;_#`v3uZdzrY>1x4`Pk1kSSdZJ~ zX1r3TWqSZbuS{mx0ie~kT+ z-)}E)MR%yH{>lIMz5K3MhFs%1y}a7x=A7xS&J#_t=P2(l@*bxCE!4lY%ZOx9=P>Oa z$+LyFr)7?I&A^tY!%^gFceq`z@O*)1b=2)Ds2d2X)Kmq6-;TEIx~VV$+=sS!!Ac+p318`t-8iYC!|-dEb^(y!uAh^p`%lZI7dV zZQg3Tx*{u04bW^3=hfI4w`^9C%a(3w?%&^jd4|p9)NEPKw{4E%hp{<*gghDWnwFzws&nV^kR<2&uxx2p$)P5{6eXnciZik_f4~3{w_}^|KIVy7`^OTbgQqhw;^>o z9h)V`(<*Z_ah3cJvo0RMz1iPHVgToRv>p?)j7F za?hJTzr66(F_({j_0h{~-W+#%-CJWXuSlyqKfu32?BeyD+9-BXk8Cy~zthvudGxB) zv;x;5mDcDf9quX|=E(Q_ROQzcjc|F23S72D#m{Mrik=%oogR5g@??>xPdrc7*XFqk z9pWR{${kd$Mk(WE+n|%u&!UV1%18sgzG&2QHT1#vZ0VdC?O*Fr>5WTxe@pMz7^J>y zb!@q?{|i79x7+eKOPX>^f7*H%hjsDe zn^yb?asRaSDsCgsd$vBs1*HR;9k+&?ujpL1=XTPlrG19(yw%Zc8`Hb_&=^Ov@9bX9 zKV}cguAGgs7d4Oj05nZCegH4*qP(3Bd*kmMy&GS4XpI*S@8A4*k*bn?g=?VQ0_@{1 z!Y=be&KZv^9qC%+cDp9wlW9NvtB&v=x;?JfMyRT@NQZ4`@!J|_0jSG}(h;s)(ynK( zaw2KBWAB?u+WCd5Y6NKyQufTH8O>o}{EIf-vox#OQ|fm8i9Eu8e9_epKF#}k>|>Vl z{FE{A{B5@8*ZBSg@gLl3=e|O%SndKiC~eVfjh@@I<{FjNXrS-=mC_rZrkx(nFbFD)y+xyqikr z@?1A}PU$taLf2iq`-|MJ0-jF(*Ya1VyIp5`3tX2Jo#C2XG}`6nds5LD*CpN}*TGWa zOUJkxcwXW?o$u569_{Ky`i9aXS4}DQUi^m-x96+UGhFT>l^-ru`EHew|B^%HZ^*Uh zdva{~h2X64nvtb1vDX=-kHseOGHgWsy#H|V?B}qDHqYAHsZZvP0@psCZvG9?0#^m` znxx@9J#VI~f~VqN!2j*s8drI)-}RC)%e6XsQT_&=nZ9aQ%|jRF=N7BWdq@O#|l}5Pqzey9!PYQbK4}aT0c%qMm2C@im zCmy-5Qs^Pk@7H2SDg7??#&`1m0erUsnW0WEaILyqUFD(f?%cW5X_m{;FeTr?+~Z(= zjK0ViX6!}-*yS_t+!s*eZvr>p<|%XK70mVMgQiFQYJy@8+qOiD`0i9!ZCi#vs^wZl z#_zVOY5e}cvpVLJ$}TaL7wc*#Hv))lQn4}(b>z|<2^YKRfxT&*s|@k z&PaT5zR2U^TQ_%h}f2Xf+7mi!>| zd*5$}J&3Gt2DUAv-Lhs99C%(88A_RFpwdaR54lI=FLPbYex)y0>QMHUCG)5@vClQ- zp;G+E5iW4d^Fiyo;TTG7dEKn@z_uyC%;hiX+Fm!XRb^fixeZ%~8Jc$3WJrrds>WO?%4tJoik5PN=8_;5noqo<~yYUo# zyqtQsGz)IC+gkBIxGujuvc3>IzES0oE%;j$zh5RzkVf!(GwH+Y$|HvCEAljHiM%m* zs@M-u->m1U^ZYwmA4B_L?9^J3yVfyRtv_f)KEw||3+L0qW!QXHP~M~9b7@O+%Vf%B z%*Z{iS|6=>8NN91l~Lt~*SL{u(s}MZUw!mr&R@*}_x1eOsDsB7uKEB~yht-wt>tgo0^SO_sYMV+w zT#t<6=g-KgYQR=o%H;l?!~1Q+i)V5l&&9}372Ho_cSyMXaEGn17(V3vF=rhrxSz(3 z@A+i7LwR4p7X^A+uN^=4$?!hP%ek|v3ie&>IVs`nzg*0@l8PL{b5p`|fREjM1%7Vq z!&1W8|9J)9a23M{cc+981HOd22_K#k?gl<`PQ`G-i&Dae10UI|qKNR(Dd9!HN6xPp zP59X<;iG{s;b#**J|+BY;3JP!j3>M_C44;aSx=Og8s2uBr&!~lf%tVZX^OrVn%Z|B z_gXUkO`X+u*jvM9vgQ$M-IENTF7~8D+7jWWPix^-$UFk$4Pc1>ITJ2)Bw?=-#`s?? zZJrJI`T&A9Yp#r;@yc1QDaSf|&uzGwzr37fD0J-uK4%yQD0qm>Z;zAD zPr0WPHlKQ43tYAI(dK$hZ@fe6-KduK#g|1|vyXe2jBEU*K41tOkIefH4O1Gg%OAf} zDM($*G*#Q1KCb0GxG3;kHS(0q)js|*C&^q?9xONKBAFjIa1WKN|9Vq*8E1b7e}XT; z5oZ?$h`i7RFFMTG76rc&Id}$Tyo>F<@T)LsHke~g)xP?1SC#k#e1|plPT+WyZKnce zJ$2t_D=4n!{vk!$eE0)WX+YwySBb7G-L8vd^f@4 z#70|aNY07~oxBIHz7w1Xj@Cj$uff{{KV}^EZ9-eE;JY1JT4)Gct>Sg0|0nA|cTq-j z!_wB~M&f--9YZfd?pzKnDQIj3eSHyY8KI?s(i`8iIg6J8f2AY6QRpfyr*HFR&?~sv z9U%YqMK#aW0>6Q$$Yvwx(;E6$WHe3Q`M2f=(M`y*f_Ym#MyuoRNaIGobCv=62fPuzL6>NZkjn3!tt{>G8Xbx zpV~{ITj@LLuhZl7D`TYbU!4Eu`x05#@qH)vY6v~YsHdFSDUe}rer z8!P>{a@zgKhl%mxd)pc>;r(4(M^U!;Kb88_?PLGTH*T~X6=3u-c(2aDE%zF zO~U0qvpV3qp$BNh410{VywF2}=fSgT8{w6f+%0#74{GbrK1)Fgj-(G$c`#s z!0o(0iY`Ft>{am<0G;(R+~_g6=Kx(`?_OsxFOGH<@U-_D?W#ckr$x~T@DK8@?KQ^K z0p7{eU>#oU7~{g;)ur?A%X|MgUPWhO;tU=jI29f&-!gyC&sJ3{X{(*Ey@bt2hqwYd z+a`32Zez8A9}|ZyN^$17V_fI-I@^`W-%U7s2E_-^Bkx0J5$vnXIY5;byHsX+vpxns z_Dhwo+wq@PbdF0Eo$V4Ha}jmR;2(Qc``y~F z`K9uajjsW3CG=0r#HSd-q{?T;y34s-?CrvFmT>B>y=@`5xuwYduiuv;0XTZPD;@otn3SnrOJd8 zbu$-DnOB-dz0%MxAczdE9>W*@fMmP-`o`{ zJSfPRGS`hVh7NaC?PX39J0#)1%{))$4;Ih;f%p{Z!E?v_#iHe&JQrF&C4Z>3^?^TR zw7p{+)mBYirCzgXV?gz8oCUqlgsbbxwWPK5A#bc{%w!*U7N`t zXvYAVZxj6C>v(P%?G^s8jyB4@g+Jggyh-uX`0M;_@*%(AO@{USwvjt#Qq||4Uh(hQ zMps2z@+^g%n+R86Ck#K9^AmEW!t%|Wmb}|t?nVl)Rh=#4)s`nG%b79GC)enqI&6J~ z?;nBJwX(;!wL=db#a80oBPW^)z@hN%gk7>03|2;1qgdgb=XZD83vMR7h5aIlYr$4o zbP$qH(#ZYLk4il9tCTgEJza>KGXkSRxrO%7HN;83i+n3QW)|PFzYQXu7{+}Q)6>)z zsmB@cNm<{&$~>!}DdAn^=!K)Y;We;tQRF3m<+HU$=QH$WEpmhGLv8=`MBLZ2>_-i4 zXf0=qU%{CfD%xy_hqonn*)$e3l3A3I^&djA}d+HFbK>zwL_!sXcy|Jc?LbKt*3a?w9 z>*9W(+20RSMi6{N)-oQ@5lUXs6PDSy<8aWwIb%?f7P8J53~sv{ID1Uq%sChIWQj8` zUi5)U{C@IfAI%fkOV`1(8@ow`ji1QZU+1HF0eE2ioit{+{~?J7#rr+rLH53-f26;E zCiVFcTBICP&+;{P&V8w`;3ID5yxOPmKY@?t>nV4k2{X5CI&Hl_4u2)Eqo!@};I^9ZuA7ayxqVr=NC+jm1w(EZe9S`o?XD&GMJ; z9A%G1Yz>|en;5eUu|vhi0=~9O>@7V0^WeS0E3tc`>^j<5&tJY1wpcmT-?Uf22Id3m zEBG?|1v@vf8~Z9-NAXKy@>J-|FM0*q+?zHFy~(%8=IQdCLU%gf!evc^oa7C1KZ5k<2Yk23>mW7`@1YCc&Ri#X_Qt~{ zzWQGlJ||I*#C;t;nH-b&)Dkwm;1OOdYe4R=^oD+p4NNy4(d&n;bIdzfgCy|y5#=Uq zjb>7=;CmBomifd9?+wFKHF%1)dL}aLbuPy7M3LXsQZ3uVGl;)>+C5$)&dolNj5kRq z&-_>I=frGt{-+jQ5PdsU_>S1K9bd9-lgxu+&w(xB7G&>xY8)>-xXkbzHuaILn~yKq z{)jeAO zoqzi1M3eM;0_G#vS@1+pMH?&M;klPIg}K<2@onchTj^8Rs`T1M^w2d*cdo^6&S>5X zhp|rKpNtoMw6a<8ycf#(1pdi*(LX2Rc`w9Ij-7upUi8q3c-{-~zr*>4?s(BhC*pZ8 z#E+BshEC=O2g!Ke3p0u5pNxl|lJUHIa#(}&FQk3q3#gHK0^N~Q_AlRNUq8k=O8Wcw zl4s4nmi`^T##o%VGfeD!)?_RyS&NMDna%WjjW27x?NgxzmDV1eAh--Ip5l*M~16S4SN1q;R7S{%QtNtrfu>VWg-36U}a$;a`8oqyM@)_ zhq|z}$0y>NTZUv^Nx!VobBk{zoH-Pq$S2<$cznr=R{uMKl~!8yZ_|GKS%1#b>CC@_ z+v>50XKXbcL$;#K@uigaS!dS-xl1LN_pd8c#>^Vu0NR630P5$7jR@U0P_223yjoPf zQ8rwC)JtTgpk;Haj@@#PvTJYT&r~19ZeN=x_!j!M@cvkGjNQ)|6Wk{5KRF71ql^LR z6Vo>)W9?)1bVRo9wAtluoh@$qN*M#jy4R@1o1fQ0h19Js*V0AT_(#I?v)gL3)f(2_ zZRO`{$Q*J_b#3X@O5b1yCQG) zya}bP@Tp)H{02A;>B<|-Q(i^<^4`i@*H3v@4kFB@ye~lyFMfEUN%*ngU-)s9K5JLU zO3K`7!Mh^AA%~Z*>FrJMU6~8MAfL&XVobyQy)EI@)oUe8E_9lzw4GG`=DnI@LxtdZjk;MjIKSwk4baHDfeAJ zZ{lCaC#1|j@`T6i{E&D0E=%lsmz#L_%_iEnTV&`&J7gXv&F($K`&a4_3*HVBp0r!i z2`>_UqrT~j$XF1cZL)?8+73;O&MS{7du|rG>IFgSe*5Y2M9b5IB z#%HDedX9M)%*V@&U+uGH7iTlKN*d~uC24*YPopuPMx`!so|9;wS=R9x6MKdqU$P}x zPnjpBjxy&9f0TK$LF!1qgl8z(=iibhGFcoC8~nMU{?xTW^&i_XPm9#d^Or8ezp6r4 z;LEAcTZWv_k+C+fjAuJf_J-#%r&rB@ACKm#u~%#6LBA?zM`f)icu&A{gX{ISRTH1= zP$qBCGUL1iM~F_8bt878T*XzQ2ghBH{lZ& z4QS@vBV$BgAKWdx@ZmCB=lXo^Xf9OT2cbG=(T8#`gyh*!$XGz;=&+@?rB7MK9zt5% z+qU%Lc6jI8T6m$|sNa2n=cNAOe{I;D7D;11+W3pK$cCrVBJ20+ohnb-Wv03UENYI{zCIg;9}Dwx(N5Ma`eiDy$gqFkp%y=!feHUrjS7~UCZqd#b?A04HEOf-n{J9g3VRzLjzH&sz zDro~(V~4(IXjV0Kh>?ET)mGgE_scF)iv|B~_S#vmn6UpStWGJ?ES8)A-a3qQ1RDL4)ItUPyJhs_ayr4w5(;t8u0Xa#yeeA zF9`hmE!%;wgwI?j@B;#$v;oFrD*W#Y{K^D;>VCg6;d73<>Ux2Hu2ugrj0yT|x2y*| z(4*8X7hjW8$)8G>Tk!$zfi^Fc^2jT3`8HKs8*fjUl=omNzThS2Y7xO#Z^}#ZWOz~# znqi&0Wjy?JJWqTvx3PZD5*xOCT|cQ5d1)L2?{C~FYyR$aATs7Ixfe_Lr1(G+KS_S%8AaXjdD(ei zrugGG7Pq%IR06M(HG1tM@E7)iy&m?EMYb&)jXxj4u>0`_5@CzbL&NjxvJ7w8Rm96X z{)@`qRSVc}GyN2aFN`U?KWWoUJ^%JWDkOSokvA^QwALKRZM$Vn*SBNL^@=eCzx}m)v*4o2?F!w;f;myymIEHJ8wxT_v?k0>&9X! zyMXpd*<<2miw~g$%=P@Vt2W;3YMG-&5p2(%))(qW&l4 z$LCXO{^O+Wpq+xVL>oE?6TE?&U5Pf;xK9uLop3kxHsyX~s)XF1DrX!;?n$i0g$CsQ zxK;0^$QjTybIFz*d;gHou8bjk`ZdK^SIT=Tedf@g;=4=cYmt>a#_1sm8%di5uA6Wb zJ>8ON=tqV8DRg^05f>Y6ey@aXk1u(~oac8SPx_$O*0b1;8Up>(_i~58lp*-K0)`Jd zs4%pUFRF!RUFm;%68~-RuFLRc6*kngR?@R?KYd22DU%PdT{~?0#l+R)1AkkEirkK_ zsC6p$on4_Ko1oS8lL;eDJ!f4GF0X>*|N_y>bzAr2a z^jlFBxZKy!a`|k&XY&05-|LDz{gxMbF7Hsm3m%4Nn$PFsPhfQTjZ1CiYcHLP+`mkv z)drW^hO#cLs^;9#T!F8p)n+hee0IGtsFbq^8>vz3w`O3No6$>oi`i?RjNPV#G^eYK z#(54kV4Lb)Yo?t$sU8rN?~}2sOv1}K zb-FQ(|HX-LO-qdmNjp$yF)o+^0@A9~;%l=r@w}8-D6^W01=x zUz?Jy=+x=nLuWC0QA)ber%op}Jd+zz(w%+kbo0>*PF|goZv3g!$^13>q3(1wrIOC} z<^F|V`W|7e4;`Qzj7wuBhhJXCB!KFMZ{Va{!FeFHEhOyW|*r5^iO2Pbq4GLKDKnmPT# zrMc5ZCsMK0Fx5(2t1jD^=HlJbsd{;|l8Mm-#f77W&)a z6NBB%(ZY+OXRWA|r~W1~sL1fVGY<|xC%9k}FwC_G{_M0jtvT1^LBR_`Ks)kzp@* z^It~BvErpFGIzMKY4))4O*3IC*|myE*Wjx^g47Zby?O&UvLe5 zL0!$h8V)WxSobx!&j~&BnaE=&uMv-+FA(`>l`_1af`bO^;N_0#Imk2(=VwmGF2>v7 zcQ_kVhO@z)Zte}V!wYK)4DT}b%cZR4c9q3EKcQOpc`w>@M`caA&%3d$bzz{N&szfw ze{Y_C-C2WfzQ(Pb%h-!p#d^`tT%sJdsVY++fZSKx0k5mp%e^(}J{EStVH5VD)N$)(eFmQPs-X&j#7o{Dn0V`uu z+7?Q^0<>Y)K*z-DvEXg~os~5c4e$Eb9$y&vMjXbur+~q{79?$$bY|Ot;eYL}O7~sX zca(E(#@%;UMjyAn!;~4My#{TS^yQ>qe%;->rLQYSKlU$?b3|VyJ`EcD6Y<}pawIM_ z+@guGA>l1@PDa+MInYPqyTfKLmOK*P;6EqSm$*~H2#z!NQxoYMV&|AX1`=tJ;gQAU zZgOZ4Uk-w6_99GN-viAi(k1F^ro&&vU%o0XHX$T?@oAs*pICUtDyz-!JaTfI-4jCS z^amTizpK)J?axgdDdr3R=sPRbb=EiQRuiX2zj!>~iMpIrXU>EFp85tVu?d+(e}0=i z>N__r^j~fD=MCSJ{)DcPL2BID(6(M1rYtq7vC{2))GXW0cLMfF?TnU&(CH^}kSG_v zu)6^tI8Vg!bQNh5{UYN!p;xGR?Jl$Zg?BxEa{Eb8+uUX zH9DYIe23P$Z8=ks)3_s6)gD5ASf=M4HrSU8FlXK{7JIXMW|aDWHKX(;%ADonNk81! z%9FkbR1&viMrrl#8KoZL-*o3VJ^nPOkN4RNXOz}d&nPwg$~o&_GfLU#$g18qqjaQ7 zcc!T{=il5m=d*6zx!<%)8q{XtO?0HgxLaay+a=&GQNPo%W0JKK_ngM{sKdUrpC{+d zr2j>>Xi(>d&h13r7nvbG_C!}CXVa{6X_ovUco)5RVvj&{d4hA1KfqV9tT#RZho3S| zKeMHAXKy}sFJdG0 z3Vb}w+4lTJr!8deuiC~PA|7PfKiz_EErWA~$Yp!;Si|z%#+@PaNwc?~THvN%@}2mC zlz7gf`+3eF{cFSvpEcJy;WGT51uGBtQENIKYJz_@=frIOiTzejr2Te%(^N6#AG=4lK-0UJV}pP#HZ^Q&3!8;PHdjC>)s0DF~oxEfxG zuYuKEjI#RlUtSQpq~#kSCwp@8vnQ;HA;jG{C-)aC)ob4-q7+GS!vO60xfpH`H6ZA}zdZZPYvySIa+~CN| z+Mx2szJwk`&hr&P3;U_7z*?0VtbC7mJGv5q`;NTBdmNr%<-z#7giCrm^_F+BqgnCU zsFD};+=&~M!_2cmIg1YwzCq=Ul`z>upQYUKvilS}G%MVs$3Cuwb>MmfGUH!Kw~cSj z{B{(}o$ywDl?g}YupauTCt8oF&^p%HpRm4=HH64zvj4fQ3|}AY0h#-s=%#*#{H|DU zgxLd<`_hBpxrMzoMZ3$zMybq*^wev#kj5NetSJ&Yjc@%^)-{`xdj{h3WDRFw6Zcn2 z`9aDTz9alcK?9$`bA<1Q=crEE!s52TZaGI z!t>FWdi*Z*Q&ra(>Z(l7HLgeD6;sXkTGx*WclSjPr7LfgXBT_TG4`LM*fPY}dtM`J znmL^JVl7j~TCE*;(w?5@d=q?B?wL8hN|J z8Q-E4bCd^Mf0A_Neph(29@-3DuRDZI)-Q}m_R{R3H{9mPhNl$&_S3GW-n7}qTqVyV z*gbxeHqE5nnVxwr;eXscK7hHQ_&L&Mx@%ppK+9^#JlE!5aIYM8P6Fo!(qy^=I0fNM zl-Nme4lgyFdAuqt>CsK~sCZlBjfUyxt)w{jn*le~R0uXQ=TIn&h}*h6jE z&iie7PCq(bxx-O8YF!=hdsTF;OXJ+v+3vZnmhY?M^O;X?=&vUH6n}axw6PcM@t|Kh z%6?+Qb7~qo;RQie?TS*rO!C~tlRBL9cDj0FKXYdbZCcBoZj5;+kzVpjo;u!MNfX9X>DGOO7Cs^jI%@3N24xf$oZmY zRkXbUn#o6Bb+O1;2ed0<>2>GJ{{($j1Z;sRumhwM++N99zK6g;5M1P|?Am=Me7(2r zXx9W8*BbO7^B-$@FJq^wU1$Tl8OjPB#n(d@;Q^tOD~!%-po2{JHQ>!S-tot^c~V{s zo-kGsaGy-N#IfV)P7_m>J&QKTfN}{ z)-8e$+F{m%y+XAWZPg}a7*@hk82K*gtSZbSI!?Wdwr%kWneS|&DIY^~V-Lpdt*xaFe zJMbwdFvQkN@(20PB7b0MIGNu={xJDHM}(IFM$zpiKg|84mdeU!I-;Ft6NuglNGE(X7uEoS?*V!@rXKLGA#QGaQ- zv{~@zrv8FU?tY;Cn%Vw}9Oa!Qr9_RKHk z`&05O@{b_DB7aM2`NJ4%_}E-W`Eu{ta_TN^uq|EJtN}yHl`^Frv&b)PS!cJgu0Ur% z`G%W2K=HFF`3?BHF@EJ8Gnl4qivKycE%Ys?TC*14 z9@j9xNZEGQoH7T-faeC6Px4eexA2Tn=C620dCI*kVV*zd8RRKE#m}>ir@>R?%(8^BO!;i_^ zO5VxTwVu4oIe(p?mum9*pqB0?u%J;R>AF|BL#(0RKq9>0lfSoQLCZs;Q3; zocO@WBcuKTuIdE#gt=us?gLe5+ z+SLJ^9q^NU&1S+`A8*&e|E68=l|;KLI4>gY%77N7zy70k!Cw;X(iy|jE@@Lm3Xhm+ z;>Jds?8!DAjQ6?VW(Pc`OWEfYP`*<2d4>Jd_{qErO-R40%-i$Up?BX0AL)Rf%*as- zo}XJ&x?@UpX(4lP0ecN$=IVX$6N!`X0{UO(?|tx;8M*N=`Z9nVume9K1@y;3_(?wL z55f-$RYs%O998rFJmb5N{VVC`B4jnid|gPmo3hXadRwt!+`yO#BdfEMC{aLJkX4#{%lwh0Ihg@6?q& zjmj?c2|krE)kpejr8gF$w`m6-6?`MF*KUWGlHS|FnZ0&!Rbl#CiHA!$5-vQ~^tBQX zr_378ZXFcfi!FA(%BYP_nNx}$!}|#9swjMmIV{8(fA4nMf_-4yHlETR_>kEi+5rBP zSA16#@Dx5ZivC+;rq{V|z*-jzPt5GC)?^+KdoHEKu2n~#7X3?$rZ{(8o^L*pokd<3 z`&Rh+Rr21zyMo^(?6d;F5g2OTF+-^+oNX^nmtSoAe%4e9d@X0rsN-MF--E1d@UQ0Y zcKBTy<;prh6$Mj77cIW43C~ORKiWBx^vKEwe;qO_4y0K@M z@_OoRK0ia&n3f(($_ijtC9u&iiCuLuebi)}=o%m3{Hny)WLfby4Z>##_Q!?F=DeA6 zhc~G~&PC+qKBHphpQbSS8xvkUeHa~}#63z}6kS~)FAmRT!CPDaJf8(`31ux-L!7se z7yImD^es(pY^P0l@$}(Jfj6YMN$S^W!CRvba!Q&t`Vi;Mw!u!b9o5XKy5^L&+(2IR zFip{;7OZkxu9apX_qfN?EGIr6eknfwM4!`R-M`ve??V^Ezv&II&h;>MTUh5SbY;Pb zmn@EEI40J8HYrk{eqkh-bzx+J(w+E#7W+46EBo|e_T{(X=TP>5>v|n438P2Q)oRB2 zz+(0-4x`bmdUKYGDw%*o$i~Y z^W$SYOq`^1z&BcfJ%aKCZiUjEcKQz=PPL=q0g83lTGIKUSFxcP4!yj_+6|kh+Q*4g z=z667$aGGD2VbhHALLkEm*-e)?qgz`;g7HRkzc-~!w{IrE?IYCCy>wkV~j-)Fk8Tj z)J4`Fl19!Hckm9Mcea8%_JW=AZOKI9``I4K?8W$hJK+bLu&52a*2#92_sZE|^fNLB z!h2Lm^fRVjhJB4*;0Ak3^UTGT5=#fezHf0W`$_@YKs^qZgNJTCjAb9#EtjQ*TJcXU zHs$Z{xA>3-PlyTs!FP+v7vKr0eCXvAK2#g$Lnn`a<(?7NeLeY5;{227lci0jz6o0> z4O*ABRhx7eUKnH?itfh;O$vTH(L>4IA1~AI^^DE>^kXHG$HVw`E5#hj*>w874jV&7 zxSMd{|5FHC&KR_%9VNd^zJX5iVgb)7K5~g9ck_dK+>X@S&d-jP*qN zZG=rBY$aoHFZ0%W;5ZSdSgY+N{!+r8T+W^!vWw{JWxnr#79SE@Gx(UnyUu(n{LIvy zaW*T_XROhzK1t>wH_I8Ic2EhUE^1P z6Dhk+co1nCsBb;_BweJ2chWvJ$B1yxO;ZqlCa?vjz&6v<=2W=zNoT;r){&>gHhAiK zV2J&89dNOKDqc=oXOhMbFOx9N-A2m5#XHQME9g73E?)ur+ma8Swu1JUbvQ+xrM{Pv z&dr%w(ZQm4thV?#SNO#Gzy z`(EK^NAUj$4b@SHb)*yCD|?t{0h2SpChxAJ9f>%ZUy6u}eZ(2!?vLz-kc2{_Ct zQjSh~_j_IAZBj3ASxCLY%p>Bj<@4k8BX{hWarB27$NV929~0-7yLHGTWeL4VSyGOq z_s|Ds`g$w{|A=rEk#1 zne%FwuGhwxnWD7RJU`HS zzp8LCebh~}QO+q!-R10(*l^0Zo&Ai1pO8lM@+KbfTG8=&Vc&$DL&{c^^E zJrm8GyJVkTXk6?@#r8_(uVc`&Ie(!qNB%T%ZO~7GyLQ^zjUTzYpc{AJisQ$e7tjMI z<^_>ePQs5lFZ|BLPozTHEWV(Iv=vG}ndiA>pLmGb=G>GvtN2-OtIgc0O}_n{3#}xL z$S5b(E2U5Or>iyl_aP@-WlYodVW;fxGfntwtjXGAOSSb8JSFCM_%3+Ti^bkqf2pZ* zoli~EA6bvBf{qA*4Ggl(A?DL4dx(C0(L_0i8MNh=#MGh^;l)9lD)F#}x=&ZgZCY^< z`Z8rTtU^^#G-fZ>M$J0v%$)}z>qlEa|q~Aq&2wWat^5iBtgPyQ=kUg<%bVoU~S7=Q3#)PhJ()Fn&l=W%BTX}1E ze+!+u;8ghJD|$8iL0a2q1#jlZ)4)y;%!}r*d}1;CvD^F^qI_XI~3+2yGRgzo_(BP}&DiDY8|& zh62mc>spt;@Xb7>vYi_HSW4C4_v#wg-`k;;B92O{1+L(d^A;^ZkGRZc|48<5(9wlt z-{KMEWa+oB-&eC}T%#|R$g$4XAxtB^}qe2?I~rfk~KZ} z&c|QRM{_$?Z9TUC8;W17>jNWoZ6tG5!bV@tFHL}M>Y-V|t&D3K)2*zNzU5q0BDM%q zm_P1QnG*|E8;fVK=Y?*!>JN^hl24RlBI}S{0p{kbily9Fg5?x=hj+s7QOkn##aP;;Zkd3>2$K%SXY=z?r1`QK&L7miRYTR0hr^e%GqX)@y+M z4HXHjT#nu)yijO7;8T&C?oo@|&$%D@nL8IIsz^2G$ODYGA2Ci$KFzu)$)}OU6Jssa zW*z&>)!3b^_NiXXA%lt=!o5Pxv|G+lT4M%2=k6Uk4f+@UFKeY_c(k`UHu@0vBgT@c z4l7Jx!oS5%QPwD8r>Lnbyq=s%dt{H$jeX07-z@hI=4_|0s@&UN z?+M*|b-CAry|dW;G<>hz>%#`9NQD`b*-qIzo#eJVvw5Z=`?D`Lz!PLlU{?}lEnR?a zWQMZWmZM9$y}I06KfBz!^15>Gig~~Rp3k4*9EHBbr_!B1cbapC$}rEVu3Sjko65bx z+emv$B5k^JB)$%iYvO6sYwH+mE9*(Ol5}^I=Z7jv*|$uu^*PXZbGrW!qGN=Evx8Y3>q&w^T5swXJn#yqQP`dMJ zcdm1a+u?L8k->9oTUZ0QM;nXt*%&V8-G)8$QSf!cZA3A6XN`OPHMcQPuLm=uaLCR+3tM$);#kg{EYVw`cmjB zHOVH2tX#giy-`Si}KOZ`#)H+LjebB@|*qcbg#5anMx?c=Tu^C9rt3_*~i%olQ=u>F3 zNIO8@%0=N365bd7nVv)KkXU$>mu(0LwNrJ1+AK!palMh4Hxm0q(ho8(Xzt){OFyF*PeTtY=U>5H z@r}%@Pa=nf*^6Vm?-m;i+jr_q2e1r>} z`gmG-Z+5@+pmA-fUNACfrAF;4#^uj>@-)t+fz_De8k96MW+{^ zF?!6IXPte{*m2`0Oe`t&o_pT;7ks14DEC!Ny6~dOQ!c(_>ZO-me#JMZRZg$^*0;ZN z<#)gL{i}X(^$dTYx@P9A*>mPzGq3jA`PW_l!vza(xba6f-Mnb=ElZZ(dfT#KUH$Tg z6)SIF_2WAl@4V~opR5kubMKn_?r-|(0}nPo^t1n28*XV`w|>LMhacJ0_UL1e|NIwE z{PI_uw`_g#siz~)Jp0_Qf3q#ReaG`Jy!g^@Uw-9xJ70b6_pk4IWA~mn|M1oy|Mcg- z{Pk~t|Hs}~`@W9-Zyz{#=%4Q#e)qliKlt#Ye;xVwlTVKx>-_BVFOHw+5;||i2Hh5? zb&;9%^S_?f55;MHUka_?viB=!9asrk7x@TkH|3&kdbiGvtKcQxe7D$TYS1yVJmXCE zA7qVm8Z@mKGl~6(#2C?jSPuV|af1Ah4s#c}&A?6Noy!(#_#skTe)Jb#L?N%DS7wih zr~40iqF0`e?u;|i({dO$TmN-@^B8*7vXE`!>Go%m1r#TNCAyKegP41a3WWSN?Z!?@YiYe=6LS0(Yfm z&3!3+LHZ`4M-~1rGO3E=SY#Gi3y91QUn&j{<1DJ_j|Q2`-#1jGi~ohea_3povI6|2 zu-gS&dvIc0h?W{|kJ#U&q;P>>E`dQ@1+LL)z3iC1C@X z&{4*2yTq$99VP9TU+Q&3w?Z3YLlpzgKd}c^!Ssc7oR`~H%Ra&!;P7bPr;JnC%ThLdz|b-^eSnM$#+Qe^3bl0L zP)$9tP`Ql(a!-Z&;`%)bJ0^iA{*3~9UhnFWf6fz`P{QiIA2@d2l?980#&a|51M+Qy zYCY^ZNIi0|%AKBX%dY(~`J^8EsE6!He6J{)=Lb$84riI3TO0ho|Cs$sj|rz|KccrZ zaePGY6Vg9p?N7hAf;UrFQqU*#UDmxZ`b6}5*COk7&>x9-Im2-@#g1I)MQB23U@B*e z+I^RL_g!?UH+IRT-ZFTWCs@zDQ=VGw|FQNi@KILR-v54PCKm`wEQ7dxE zrP^jPTts3|YsgW&oWmp&Fc4ddXqzaSNr;N3J#~auE52otfI`}ydX7+=)AJq@QADxl zAhhbS7ZN~0saETaC^ql+_smQ(Aog7T|M`5L&oj?{_OtieYpuQZ+H0@9cBm!S13#R} z7xnhpJMdU2IkN&=qw~8iCm$P2IUOdOXvO3b*+^Yb-*K+r> zr@xhZVoVZ^3w=XF?|@%qRDQVBiQGePikGAHb#(Xxwrzy+tW?<~ka_Iayw@<66o^;Z z+l)`PJrAb8seA*Baox|!KE=98CiAWvtt)rY?(OU`xXZ==qs-s=U&I=HA^BZv*+)XY zN6B}Hd^)4$QSu!kpY|=((O0MJEi84i-*I_DzBk~xc)s=gUuWklDs?5)xe`8!*O|)y zBFb4ozNcL5Y2=Q7PR4xpsTb{?T6zQfja~SV?-}RJDB9~Tr7Z`NnJWg#x24`)s=aIm z8wd2Mo=^PqDJG@8h<%;qlqnjykon1z<{xbHog3D^Wi!V2z{<2x)eY1!gFMUGZ+tI4 z^E38NFI~Z&;t*xH_f9K~kjK4uN@*+caLa*1WhGPAD$3eJS@=t5ET=5ib2ISc&j^zL z`TA9*g?o!i*HMml?~Kx?iJw$Nxm$QwM49r9P#O2d%2-JmcTq+?W!yj+b+IypkGfbH zyD6iPGP-zINEurwgBVZmte}joz){Eds#`u5>!8~feCZygJw^C#sNSjgb`;^S@g2Sk zpkrtaw1Bbh2z+0M|Hc-092?P#O83(T@R{1@0KbR9rysl~8ArR`ZFwiZw64Cklzufg zpFO^p~gpt)Tqd|!RY zSauw1=U;|)3Z}NpYfG22w_!_cZ^J_B6rL7T*#5aM22agDC_K5qndWGEzkKb!O^agh zx1O4JaLYS)9BgwXwbvQfz9aYuyH-Z?%f?bi-Zt{wMV>7#SNmAzcghzp|02qZmmehW zqdW)6zmIR7mf3kO@JoKH4U%Vkd!iTnoa{nM|2OW5aw7z-3ZWJIj=P(`gAb5!bU(4D zkRRxt-2qenQlcDDzBTMimcHXLW5a6}UpBbrDHrlOt9#4uHTNHhz2rmgUiACFWaEb- zU#W0&2s;krYGEqR_z3A4zYoQVEW__8`d-hRvxM(a^gT9#LitVM3uJ$X#@-V% zx^NE9oSS5quV>ChD&V{%4}1KT<~J=5%h!l~#$I^q%5vf~5ZAl=a`;C)lZ1@1T+#St zAv0x@37Hq?*mM|)St6Vc`yT8zVeI){3)_}#?{%!%Y{3@FSf%lib%t?X*$j^I+>ziz z(LsG9bGm2jwTgqk8?m)%eqh&w{)(N=D?6KfQUb_94`W9Mb94U5}ifCx0>QH!w~WOjJk;5YP)8tOIcd57$ZjP(uXuQ!7nv@n<- zihkRHw^san{2NZRz-yo0e$cMZ&d>PaL%v1e6ScRWJt19`Q%k)ar1cYPuZHrPDYpZ< zD85}SW#iklk^YmNX2TqvclQE|BNWx04Di95JABB$fBvodP%Qo&eMojE*`q?r|D9;O z1m$~=b`hUZ^6_!x<9<)zboqEabxA%-F1|sT>K7hp)ej#%<-o4pdr??%m*t-*{5k3? zdTPzKV$Jn8V6!|#zSG}5Zom5u@30L#7^GgC*0(=ye;)+z>hprv?mx{@IzO>4PUo!d z4e0NJLvX2YpI&~%F8?NAqK`i4tgGl*$2#FM<^b9QCBMsMF0=Y8{1?A1=X#M#WvHJ^ zr&K#!w4=z16ubDo&NLRg;4yTAxoVfjdiDo&Uq>4f-?w2;jlb)l?&oaS`nFv``RIp_ zJHhX#v@_T{RyJWk2zFQtZHZdmZ^a|=GF9G9q`mxVLSI8Z2oBZZhF`=_ffyWiS+V*J zF*sCrqMq{U?LQ`#cvKE`9f`)CjW8B#o*=00mbzCrw+CC}zc*8Pwf+9>G4`|KNL#A@b7{UvtcTe# ziG&BWl{hxVg_Jo@b1cfVxE~=tb03$zPP-xMQ*qt<^ileb#&7K(sG+Zl2g!e0Tp*Px zTR>!*uSYS<7Qk&(;`yYMt%3uG+PhWD!${nWf=fGs= zjg=SA``7S!w=FY`*J52S07jzOH9E=qz>rC9y2T#M@$Nwpr6&okc zLc6biPF?T6_dl=e(0@^v|8wg4&;LbTPuq2k#NE}{cYCaSjazYf+xlhpo`AofzJL1M zQu){ZmHZv#H~iu{M;&<$w&y-$u9t3LpC{pCe=>a|pY@Wx&nK0ddJU{xze=9%o zdPfb{lvv++jlR>0teissk*$4!G3Yfs(~tDb{J!UD$x`GD^2I(cIRee~E>ZSj!$UvR zH#c&Arn%n27sqY<`NlM{=YuktgVfmXLsf@qlk$0Dc`WiEuWcFo=;(!0Zgo6dPF+zM zzU6coIGp~t3CHTUWqZ7@QMN5>B4&EAXF6+u%r`3-&k|uZ`~oRvM+Mh(Vq%G}_a>(n zKc#QKh77|WNw^ozeUN0g(yJNUlSk46q}zBY_4RZljiiT2e@6K)Wo^MVk{%&_kJ5dV zpOD^5`m;)(Px;P~{Qac&JAL+C#J$MZ)1v(`z@#*r7xX>_eE__Bu3#Ld4aix0&0>Ib z<#CfIvE9m}XYo&bosBin9ju9t;DJ3DHXfD&XX*$%KFYH3a6ND);3q)3jfVhuNWf2s zbQ=$K;2{A&5z=iu)Kh*!dN1iV9&V)k1pM@qK8%NNf`=H6&cp-xcDQdzz*7tdM&C2n zvGLS+2A*WI{cqyQa5f6`9L7_)10~7&bMqgP4&@E(^|C_&>%E;@KI-*MmLI<^f=4ULEBD{0Yli z`7fR=K!@$*zvy0o9(tJn_4$YKqX512DbkNI7eQC=IZRncDB~#KzQ()P`R*9` z?|Ccgr=xK!PNVq$#PKL%ax!1De8s}YYv7}qyW;5VVuzs*9jyo+nDT4J7DtILd zy9Yh21vuhm>+Yky9`Qf4G}eU7ZO*S1cS75BTydCjvD&S#lAKXLh z>1)AjC{(pbv^KIon4W?*-(aCjbAR?$1?HD^>0UR#Y&UmsG^jr8SL(1_%=8)Gk6=810bMx8rm19t$p zp8)PN=kkJC@X9~z1MC@bc=LfrumQuTllZUseTe@CSQF>?23XFF6B>c`Euug5Mu_8| zWPP)E3U%n)1)@vDUhf<9Y_a3+51z&g(9**d*Lk*nj?PnXRQgM(N_1|6*FBnB@q8w4 z1%RW1tANX%*O;7Q`aSXwKTGKD@%fr+C?gRDH+hF;p52DjVh=I4e6;mUUI~#`G7tZ8 zdky>?cpsQ{6n%}e2`FE(J3#q*uR7qx9g;1=+h4D9QL!h2-<0pObq2ssMFNhVvE0Q| z=*PRpU@xYA;bRK1Mh!j1VdNRo`3>UU&zM2DhLC7ElK1gR+s8 z)9*f4{;}@jBcFTv>GF?u?=bGKl_j>*PJ6#1PIFry>?favjjaY7op~o#Mr3}RCUzeC zyXxUI8+Ufv(zB#pf0neF{?qC-TVi>z=?T6NFn|+mOVMZOlc{vc<+wdieAG)GN4no$ zr}mk0-bdx6?y}jAfB7%W4Mxs&)A!)6;YiG;^Hb>4N;yu>i1V<15lnsR;c{&1>lsTt zYaRAEakAMbmO&qWTxEMA8DY+sWzP1??ah?=`m-8`@=ttBJkPBV%GVy|~Xt zbX~^7nc7|K#gLszYdgPSE~EKU2!81-l`K$R>aX0tB z^p^*_$g6y{ybo{|kVSv+^ZrHNMQw?v*DD-&+?nAk?Dmh$?fRjI>i+?7h&J)LU5w`r zXx$--Kb;qt8NM{}d)vlLys&s1_+D@b#-9O)Jy*2tnep?HM(Q=tQ1yyV-~R8^-Jcmg z?~J-7XH>=we6MjsZIPWaDqo{@3ZIJ6p4f(eJIOvp(n91H&uVRA!%Kg$W9`QEhzRK| zT#j*P=T+J3hjx$SoZ%5WF}9oGeZ<4(4*bk9_yKV4z-}lyIyf^&FfIc>`Y(KGy(MVl zOZNEj-Lm&9&yVpzvDaUI<{w^X>Vvlu#~@&^=Y}u;hI3JY*Bav(bfh~5o#PyKe+`gk z#+=@F6Jgf)EZphb8y_-JxM=Cr8ARi{B_oL;FgT9;4>Em&-{(AzLk`CYCwhb0Bv^Oi zQ-08qweNi3|2}7QY{)cGpE^7K?6AE%+W*|+!#|--!~L;$TzDJL@!0qBx67tJ!KD36 zb(nC_W0S)$lgJHTfzF$D+|&2-Hx=a z^grT(devvUF2YWS54U_oyDswC>7D1B!GPPD_I1*oq^oZ#zw(CMuCyCScg6B|o=-dv z`tSG@Gk5c7#vFWF@FTgu0-v)LTp=!(aa9%|XLcKBrEl%nB{f{BJ1-k-=e~lxZ-SpU zpjFDsKxwGdWLhh0N`u^8D;rAv-1n@#Z1C_}M;`H&@0mHiWc%l=Js!@8=4tJ|y8RzI)=QAa=EMvuy;ZEDj|@*L(GMZ12Hi%l^Gc;!8l)gECFYd*Zz4?XkYG2}Ah>u)VBg8s?)>xd6h?_dp`yYSj|r!#NQT34RBtX+WZmcjU;gVBr;F^FMv$#&k8-PHa%nV ztK77U_+e=QV(ayBuX}zcMu6C4=HyKWh2yy2WZ*VWV{|OoW6#9|d+!MB!)499ymnorwf?0%L-iRhO!YUXa3yigCtg)>?PHq^ zapu~9BgiFqo&rB}fJtYVZp62B4|l;=_i907@U}UPfmf$Cj`%P};S}A~cT6LDIhWQp zI^k>A(z?bE*qeSKH0VDVjlrNez+b_SvIe}UZ}!pu;(a&XZ3`unRi4zG%H9`# zax;IIF81|d%j9|02rSfv-W@o~}Sw?e+Ka)KylSAWene1k) z*4`Y!C_PPMfyM`W48av{FA_DtV{Xnk2{sgHd{z`e$ioOYq=Qn}OO+dGjx+av82|Hu7@q?;^5o(|I*eLSk4SYB*a)wTHg z`0=$;9_iVk=oo&#ClnpSHD+!2i1h*Xu^G-?FvRAn!EevK-rdex^-C>`=YfK(v~{eD zWWn3Q(V6pahY$M>`?poL%F{L89{*_EsvoTZo|w*V+eG5_kXG>DN_kIH#`jqhD~LVI$GYPi z@xC`c*7wF&s^6)W~d;r^Xubv4=6rFg~kY9>ykv{1RUm zB3sSOd1;36S#u2O3d$2;408JB+57eaj5Q;DAbM7*_FQ}doT5dfT(&3TTu@(J9#oKi z75I_Qkn~6o&)Tb@{8x{|j$K7%rk>8p+AO9dP&DT;G4OisgH5xNqn^r z(LP@}^aQ4sR_q?wIXgF-o~!dxiwm%O%GTMr34e$0nVuB%3gz!*ABAwEv<~ui=)KCF zv$%;lZ%HTr=drivFned4edwf@a8|9}fCH#y~8{YE1&u;01gnb+O%ZPPOwT>LZr zatCEOZfvh&f4R37A9l?vH0Sszb_QyE&(Su<*Ue7X>eRK^WaH(UNe%06H`N_H2NYAD zK6mLP(=%f>dA==sjcc`MPL%#@I8*A0Wj=ftu!qNd9-1B2iJGR^d?lvu#Qpa_y*+xS zs@ggp{QritM)W^teg*bC_U1EAOAaYb^&9tsrV`p}xkFW|chdbWxtpEY4aaXa)!F2| z9^Z^Y=&=F6zq?j_yL1;g2`~qEX1bZ%xygs_n%CV$3=!g4c(;5H9UnPHAAe9WYjeip zheuf};pHL6_=Z99^iRX@1D`U76Zpyo`URJR2rt zoZz-`i%;kbA9NMmZnIjnQTw(NFGqR40Ufko`s(vii^T)(3&5?u1t-1dgOfNdu6M4! z_v_Je;xt}EIqlF`w2jkQG!}hD=R0FGzK8OM;Q@x@#6A*EXM;}xdKn&r2+MShpFt_i^>d$K79H^TaKG8rt!Z z1KHv*Rjfs4KUkbv9I^9?53L62+8xlj99=OoH_F3n&<#?kKi)^4`+MSnpetz1maY&T ztMlx!vld-Jl9cz+I$RU@CG zy1{txF~P=%>YPtGq1!GSv=|E{E3BorHkzg1YLx7VAVVUQb!~De^ZOGS)6qF5p>teC zS=QRzyd3s8+3nt7dM0YW6KN6Z(zn^MXO#)94t(sKc3HXZ0)GL;M&x@HgxC9ueqtwf6dn$7hUs>#=?uywPs#s&&6C9&yUVG zk^3&%zw3cf?`r>hWP`8g5p)H~9_dOsPLrngmFW&1pm|yczT1*#mqGX8{y_hJ?rU1V z$m`Y~VD*9Fely+7d2qHV`8@szO~D3$+!h}C;lU(xSFU74AJ38zDl;x4)L)lS_shgQ zSYerQPtXq>*f!N~q#w%0g~GHkz%s9TswpJqX?{w7Zt_q_3*o9|7MR;L<|gDSaKXPVctF z@Up$q48BdCkI5q(>79jK)VIp3Z(is9QQoT^l9k&l85i)oRvyXB!_HC2h+MB^a0__d z%sj*aJaHMjoo9R8yFV-JX56#&w{21V%^r77I#-j%{*6fug)a<^jF}O4SbOX3JdZe6 z`}Pf055hBD$hu&TuP69)w(TQTfQ)og$1eKXF8a+b`pY<;HRpPn{7+`XJA8v&&)j&Y zW668$7nBUy@Hbg``7bzEkK&vHwdDfZIY2wbJDMYichn}~MR-=8%W0qR;D#?G+oEHU z<@KjJ(mbhF8uttjmmk}QNi}Knh+S)Vu5~#ocQD4zb62F*;n!S4dTplffkKzFGU)cD zh3?GC3r;dipmTc-{kFwe?N9B;S8xh3NQ>EX)M+w0)*wr6qJ8b$*Z6vNAu}cwnJXV0 zYp#?mpUrq#fow>oe`nFR#bbi`i^v3}&udswT80kSK_6X19doW*(U?P<)sBs`mZT}a zbVYr$@ym-2X7J7mOkCnNrqWExN^?-Iq1=jw+S1wA)i%1Z&8(T_OIt%;;RyR7bL$CS zmE&aXN#*RkEq}>XG5*n5tu*PPnqLXW!tY0nEjx3~tUqH9&Ua2~2w=zQXjg2lXc;FR zjYH28Lw^@9C&vGp z|GZ3EVml4rTYT&G`O>oZ2A}0@n+F5V)$S*Us+aPt@mV@ReEiq=eAPU&G1R)a3_as? zxrW}N`a{$sxh8mh=$PuyNh%{j9>isVbPCCXt?=EMG9icaNKaBnq6{DgUAA~&9OdEj zKT^N=ds?)9yz)IbUL<$@nfb`Q21|UwPF-V-0q? zN%lN>WNs{aj)$J(yh+gf5yqf3(7c)N)#|u#$SemcK>_pOuUce4+@y^Zpe21~wm{hCsDQq^_I_-I5!PBH_RJ~gtTBG)AAQjkfqnaVaQ8<5)L!Coral8PPFvoJ`=n zJm9_yS#lZWZbf!2XmKwP1Yh0 z6Hk1g{>D-#&;9icrLx!iu|Kjtn8%q4dCy?4e|qVS*z6k`Ws_}*+3N$?>jjtQ&*C@Y zqOF#mqcsHS=(m8MNp9v~(0?(uKZA|K0xsdjgIpG$dXQDZi3fYH+V4ST2?uJw+WQFY zJuF<(UhO$c)QN;s^_6(e>{Qp02emiBOW6O53U*nBzCuV6)BYRb{)o;ZM z9^ZN49PBv5&%L?faf#3UIpOrg=g`FPn8fGEq;M+FYUgXnq{Q+2I_f{DJ@)as%%t$A ztbN!r!fUBe~7LX2UvS^vbP2^ag~Lh@2azUg03;MT&Atcq34LJ$60Une4V&*(iaog zbm>P2pb`6>LkHN~bGl6^wic-M?+9n5w{U$3Jhx6a8(;Dl*!wr0pTk*XRes{Z8gIzE zzA7|*`_1epZwL`rFhu-<0I}MPId4{Ih1KJ`Z+(@S5qrdz`w7r>ODU49GXFSshk@bdv8D2k30vy&|YOmjXa0E z_g485-ur;Zw`%0QSw(r%6Ux8u-YS#uK6Gw-Rb+iMf3M&dy!S?*1GG2rJmudv@~r%! z#Xqy*(|bK@{Wau`)*)Q_&awA7S3Nu1Z2Ug7WglK~3-gk4>^2peV~kBL?sesq7|t?l z5k8?&3-r*pqQM^qMraUvUoohn>(Nh8-sV;Z>#!r|ipq8ue?QY}HtJl-`151rPo!VP z+e6jj5A79Fo!>~vd)^GQanmFou+`cZ`x@@HKd&%NH-i04F zGGmS}Dm%nuA7NvvndR>Z6#I!)k8X0V>8WLoSp#nc(O$;Zi))B`?@QI%l^Lz|(O&H4nm1#spCjAi06I#bU_n#9%e7j%vYFYE z8=7o-WFK||a}+O8o4Jd+ma(1}N)P4Q{yyaYyu39wjFd-A4avNa;*9e>(Dg`n8}^4l z#u|JbBwKfznZ=Tu!k_H%o=8D>^SkI@?8#wX(S22k*{Ho*Bl30kwr9o>E01sD_WOSM zV^CHLWvLubq%iyslyO;!8K?deUyqRuTjQz9jru+)7NVoOg}pl-Xrleu;L0w`I(KGq z?R|^O0*n1Ub>MCp_C?X4<{y|x%qC8+(<llZeCpG zCuV^9dd=VXdxZA@@q&6Csl|bBVT&Lx!oA3l3Wq6CyABqcjfzXz!8(74wGi~k69c&; z_T2cIjn1P{c@EB}Q5`DZhwU_=F=1kQam}n~j3_^QFEc1p&qdhM=VNo<%-Zkq9AZR# zp<=Lr_o}yJH+#SNKQK3%S8^c0dwn1NmHb>n2Wm(Av1j4)li&MiE4BB}K4b2~4tS!U zwOqByaQ0~6N8m(fqK)O;>4(ADk46(Ghcl_Me+te3Hj*HD3tg!tKZjPTTd@ThLo+Lo zVLOM@@Y^BnQqq*?yR;$rql&@8kJ#ryyOmZ8{sQ>3?SP*;s899uGd~gC_1@#28h(ql zn7w>^KY5gB3Cc;-Wku>eYJwo}x$FstLMS)U$ zCDQ}3buQspd-*-!tbs9G`$jvQ6=ea^x6DSb`*3V=XG%qx;r%jf!ajT%mZ{%Xydk202PWJv6@ZaKWAxF9?GiFqK1@pTHfDw2zTUhG~ zAa5iij=wcj{Q>)HujidR)nw+&mxr|RCx&(myqR4rJW56hPrpX4R;di~VPlS#LH@lb zhN^!<91qEQy{jPh`rq;_S?{Jk)f0JhsQT99Lpv&tk1ZCiYEGq;*f8KTw3jvZ-<%rS zDOoSv_@I$+b2oh0@yDU+HpO|T{v_}K-)0C$%`x1tuQ<&?dpp2`Mf?3+iIL* z{&YZoWY2NuY@6&AFGTe}n_nVD;Trg5n16gz!he_e+&d+F4bPw6{^anQ?gYtk`h-ui z1ewyO^-I=bv<^0#carm*%VEnbt&wR>?PdI4Eyh#4 z#Sma<1=P`=(Zp&#}lfBPq-+qARDTaBLjF)_)Kh_f`+X)>o^pF7E!?Htfn{OIf`yjx4m z;Jp>vksU;uXmY2Da>wWfNm1LM=ozIJGsaF0=$^~z3=54e`&%Xvn;RjqC z0FKP1UosmHJEHr+g$uuLTzEJ4D$+MQqvy-m=l!B@4|2)JEkZmiv{KeHcIVB?H`xsy zm~8V(I@& zu5a+6p`kg70WX-;#%rC~?F)g6Sk$X4h;gC(Tj75s$FYKVq zxSlD$fRXjW=WKn5x#=AG#D10UiA)PW#2lBo3^x2w=0o_$;1D#f5Pp@fPgD92b0rUI ze~#@f9bZd#|DP+2b)UoCe}H&tCt9%mJ;i&K^}R3KaYem9~cpsQTqkSjt`cruN~=#=MCC--3U{ui{mGS54YS@Pl|l>sVTU z5U#a;EdI38TXMe*AL%{#vuPWzTfF`-aEedkFi8LHAf8+c{!8@DS#2tl_7szkeU;gV z9eJ79KD-0Y_0mD2c=JsU%Qnyl%|uTR{uKSK)+W*Sdd}Gxz!sn~R!ZJzjOG7(*vRB7 zVi|Kk_PcJ+%nQR$PEg;R&|OHrUHnIvq@HX${}-^sqlbF$0fx`TKeF?;F?b~>c5?;H0YlWikh(67!So5wC!e?F_I^+Q)bRWGehlNe ze|q>Gocb%2PBL1uS$cOb@>z1Yk25NyduJnuC99>sNH%u_4SP`2ZvG8)^0Cey znO#~U*o5OX=<@O_)Yw%s%T)Iv$0NYxmTnDC`Yw*@I6lr2)w2iw9N>KivSZ)@Us*Y0 zzR6-7Lnd^bZ>pc;`y%`wTPOPvGsG6NT%*5enas^?oX<@p)!R$5+juUFRpw2Rp9DcXY7%;B{Ps@YN~!{S(fD zIEl;+NLORL+=4As_EE_Mttm%-Y0%HHXJhAGiCwE@0%KQt^TCJLHXn>!hi~dv<)5sz zfvAiLu61qNvcxy|1DCV?eAW&kSL3_EH^A2(xlVbl?x*;_%yOJq#MqsSj3~DzH5lZU z`wo+LzG6q>+n0tPpZxmF+8^7#guePq8kZtxFRw3a^qbuFAhD@b#^rps(&cLk7+({% z;<O-wfR&zl^S5svY&jr|QBsteBbk#@X;)*2AQO3Qq&XmTKlcLpEm0 zTFzC3J+TJd5ZA@qhTmKV_%X!N>~ytO)*!zN+)w2i`t4TeA={=MSG?%yTy)MkK6ISJ zlrf(&Joum1@}GO=BG26gEl9l|RpRd~t)6>0ocqw|G}E{iWOv zXu-K^*=Lif*$uH(6bHeAnr z*Cl39zBx}o=<=ohJ3)NyHBxIvCYF9 zW&Q9;0Wi3nR^{?1FB^RNPFJ3v^~}4QSzk-H@`|ttd$9jT@Q->5|0vPGhkaMJUAJY* zW?1l?;-yjkborG0ILc3IpT_Fx@RazegV?U}frwlJ9?<6_;4uQO_@ANmzYG6NKH4B$ znr}vVPC9)j`Im8C?B&?vwvb0Qx zkxlUImGTem^WuvFO*){nXc~HVXlI zdP!FP2AWNQX1$KwcKG7NN@#VMc>A6B3N3?H0}ga1xeOx(LSG)<|4lZ zZq-M3G7gF6>gy5c)&;Gv<=Y5!`XaQm=_YzjG8r5B?&silRw5h`Z+HQ449hT|H%uQM zdG4PP{!-#|XhyhC@*q1NWIXDGzeCNAJoRgp`6PPU2gsH#aEH&# z<0n~5l+0nQpudmnHt@Ro#Zl5Dv2`fbYpL!B4damN$!b9Ful@vH``8|<^16SC{>I)I z&H>26&qOw$N#0Y8F_IbLSJo`Mzr%XqVPu~49-A*yksZhhI!U|M3H;of89zob?#-uP zFQ%_v0c@d$+e#xk?+IR9j~@i~@9JmeAH>-Y^WeqlCbhDI_3A0`5v<@GSV|*L9G<}KA>UNuyycFuz`hEvKLA#gUV6X3MeM<5xMBRRq)BcDv z&u()Y{})?1?KJv7u$2IIl7HM4&ouFIm=8{vgpZrv7THw|L&xp1a$gzCx zm+P!B(GZ%w#vHSaKG6v+C0E4%*cUSWuDdEd$QSh+kH$&*hW71o&U>1Nagw#;IoL~> z4~D8%a#hf`R1RsGGm=7;(yLV7O80Zbub!0GR`_)8Y-H6N#QB4NuFR$1NnZKS@6GLm zZ*Hb9Euzh$jgK~lz_oDh1BZQ#?;-H_OY(gFI2t{Fryp3<2V`fqXrtPrHM$Pk=SQBZ z9S+*0b;bc_fmiknwMlxb0VeUVY}~RfO2&#m)jtRE%SW%voQR$&o%1C6r*thh^DgP7 z(n-hjF3Yz6j_>~GviQ7oJUai#y!0oen@VIqXT-<+-}d}dXLQ*%y91o{+Wx3~3&^MW zr}D?=pM!^ALpuI}aAsC{yL z_w!$5J_ZcLOoE?w+HyY5JF+E-XT&=e|0U-o<4cr|UYj+8_?zIm4?TAP{?RWSGt*7PTNyO;wgM!5K2=d(S0qjOUcd=O!tu#0|4OoVoy zb3*%i>>VBG4jz0kYZLsd;aSEEouzbsxcS7;gO9OBybJsHbLa^L$Y^X|Vb76erTA~z zK3qlb@qcY9w&V! z4%@w)Cm`64p9Qwmug733F@s4KumR(?7<_wxZ(9sL;#2U>H~3hIZ%}cM<9J;!Sb%Rm zIC5F8%9i@PC9MYAF?$2GPtjq+9mN;+vg#t>_5gSJM(65!~WLyrcsiRWU3gDsCQH~7p&ID zuOH)Ft+_(^DeTtR^b_f=yhp%KoSvi54gNRutoj^!?xn5qviBY#*8cbCzwOeetcLG0 zW=k%p{|^ixZy4Kb9gzO3vFr`rzfQl_c*h<&&YWLYxrgx;TxG5)*psU<4;g-C*FDmw z<)7_LTQPM{?(2->6Bx(k7o@TCC49s=zp!#ma!cjGf`gjZW?jV?S-?9Ve(uaaR!5K< zYe$>Rdy!3&)me<29`dd6_T|2f9*r%s%wv_7ux?l4$&+qq%C^$BKKRQ6&*+)GGTC~s zG(GE_fSSqC_b2(ED5K*xOgAumdiy@xj_KjQWa2NWPi>ZN`8qOC?Jl6LYUg{5?`wcl zZGDC^1?L{}pSF(&`K!Hf+Apoxq&lC<#~LQ~PdlCrYosRzuo;7I`^=~yYi^q3<9~JF zR`k!7+t!?v4eu~_@EiUu`|X-J|3zcva2t@n@%=bK0-`Sb$c zZgymysQH|48OP_)$A-UOGA672aO`{J{egew`=QwP1GG2Z&bWS}u}AA0Zq`Ci(pIgl z*0^RCYfj-|oloQHdFUq^vxYe1W=FAKV?&iaMoR&(BC zhI|V<=p(~zxAlVPTD)~*MbE&gu_YQCJdq2+Kf%rzp^nHG9X*2gFQw7CRX(=m2M;B* zAwE`WjW$?IM!qqL zel|XycVY*L`UymRYw9UmeM~+yT1$=l>;#$rhOqxf^N#N>qmD$sjSZ~d_A+i{otjo0 z$48tG);RKKZNQgf_9xSdM_o{8`~yeuJoWh{L*>DMHr&xob3 zd3{>(l7#f9V{kq3+O*=YB&4sH8pF@gX~l`YKYL>7vq?u54#V@qX|eVlnO1yBLb@xK z|B1uXisvSzUtSO^e_&d%e1GD2y(gAFn{@g9#MAF8ik06#t++5DeNSOD{fR@{^5CILcXQ=>M4EAAEp&&C8Q_E(x3SKwBmCT()YyD-}~LP;uO+{ePN{AXUSG- zb(>LUM;3bhL1HH=-t-61N-|6K_d8zFoMycJ|2F<_b{KdktNWFstVQPf?f*Z<7T%oe z7_7M}`uv!lui|-e^tnsVi+O%E>L;>U&#!Xk*B_$KoAmq#o)6FK8E^(l+>Ke8fy2(8 zK1Y3t)tJfuq;L(-|M>J@9<2F!^qYUsH$Uf_FGlOQPtRYZ4oCF)Zaq63Hhl6gl6;fj zPW#)8;}1m3xI=j!um%Iv`#QL(Sr|=Uet`6aQF!EoVarC!i2MB{`t>ZqNmKKqC@zhC zjopIZt7%Cb|%thhzUXR9l>BQHjhIOH3__A2inKVDO%YMez8~l_vk4y8&2)-XC zkQ1JZJn{Dv=V9+v$+dRt*ZoboOkI<(#-)U{%e5ZrU$>rio+W!5~*vuG1xBr?k z2fF~}?1&&^tmM#v>9pA{8`$#KI=1JpwYGOL?^OId-L$G-`yJjUej!?H5Y$dHWwpU+#;O8B7xp5)z=HZN%{Cdwx zdkr4FD>S)+_0Le%4|vC#!mQ9_tA}~nI6b$F)>t&YdkN*Lp5AL`OHa)x$3~mVx1z&l zU~Z)h@h$6gvs(H7Vbb8a`)#-)7lr@fm~7P}XF2rBhnm=Q&DayAz@% zG&Ag>e8Gbs_3#-TfdrUB=&Yx~ne zxO^YLUp@}^r?*G_TMV%Z5S5uJ=Df_1!B^X5GNEmTKeaD6#Ck}+aWqWFF79D3T{G(; zt?*wEpOFA@;8IV8o;RO{_O-$b%ege~Mn)@cPG&3XCl+Net~4BHTzL%JDQgfrSWoFO zoSD88c@6*4U(mxIKQ}BN4+YEYt1e^C212%NvefS}Xd^+E#CC1BfxezTAAEn;1@J)>E@wjr% zN9^-LcF0#W9{bSd5%b0Hay#Ehj68UKhXr3g#a;E9hluz0BYc4wGxbgL!%$|{&wt6ssPv0xYL1#WCG)*^gLkvT~|LwpUB zVcS?1nX;f_j9_nZUctHwaBFR$X41Ta?9bXnpRjd?8s6)fZ^9mYo0YCJl8$rE6zgW~ zj--_KWNZ#K@S@r=pLRTbf5)Z@Y@q%z=0&IFE0eA@5192|Fgrg0cHp%bUw%zl!j0C^ zj=q2ALHgE<__em{`-LA5*|@0_ZWc}5w-DUS126t`;fJ=kbp~ECW&6qsCmG7-4$kI* zk3w)}KZ7&-nfmQ#>bIY%zXLm-{E3gAY_{vQX+phse;BPdktV`FxO;IP^{W11yo(mX zKjqqYl?xr@)8T{`N$|vO>;SFEW%-6wV4qBeK8I+t!8U%#N87Qx@4032l0I-~L8Dgg zeX^ZGqhx4P4sCSiEV1U6Y~|e5q!?{PXKb}QE=%_{iGIh~J1YCR@cFtk$t#>TQ@&xX zHJHzd)I2(V~e??=9>9w&CQ zWMFd6kCs>P$UsFUfLEQ>H6`E7*$SU{py`zPQ}!)4N$oA*Hs7+? zm%91dnxpxp_q@NXwAmz`xB*;x;1RWRKlrR!n|vA{N~cRrGMPW2Ji*b$d5p5@K1-eL zcle1M6He$}{e<8dX zoc?UC;*tS-=J{6p!*%TGY)f^r4(jx-%W-<=eUM+e%o=mTg3f)wFq$>JH?V&n1E=$o z%!^*v=ytVtG5?>*j<&ah_Rbq+UYxRM%Dx3wQu}E7XD#i+=jlbYwHCf@p-PWfR|&=(cPl`iIjD@x*vreZPRz> zzG?1zxXt)B6w&@8&NOd1I1sOgsBb%CXzDp8^QSRd%%J|q?x?VNpxnxDI6p=QFL=3u z^IVqWOZt}c!tSqF=S1lMU;WX!E_aA~5j1Fn2Eu*Y7?XL-ImX+T?()vJ@qW$;;Q>2o zwiCQ>x#iA-m(lOVXN&NezdVP&51%ESL%ccqYp2a?$P)069I0CP(3VXrkRxNje-Qap zARe=6kbI&RIZ^=rYblR&6U%C$LjnDoaiY4eKT3zq;J$+T>!8bKlWMP{qU)4xHlrG} zjw+hG32f@)=(O33@MG)x<{bwYr}~=efbsG%zNVBr~C#<4W9+!vygTaS)m8ouDEcY(zAeFV+7@u)d61+IoOPholo2A+@@?kdj&WzwroE5 zsDrQO)2=$?;e6UvNBic}t~%P+kFU(a$Cs5Jg})Bbt{UK9*t@Ls5bd(`f4}~xHFO{Y z7SjGhW^_Y^{?qnDW=wF=x2OJ_FjoT;MHC`jnyoQRtHwG59O9|nSoVSPb+H|%ix`n^a*x) zT!)qp+s~M8(0k0Hn$y2A9v`*C%&zor9QtWp7 zTcdL~O?M~rlP=cXKg3?%$(%&~g<_=fhc!}gEk*#9mvo|KW!?Y zJ?!zA<&MGDr}-H0qyx`i|7OBxzH$DS-<<7ISE*o8CQcr?_1_F7xyy89$EQN<}%}Ka5Hu;OEnkwGd8=HJWw?+GWJSW zR^(>wYuU>?t=D&euTJpw!mGpt0biZqO8l>}dYJEw75*7~r+rp6t15be&_4JLc*A7P zs)2874$SZI5F@ts500K%<@rZPkNTspF*~#QT5GWR=A@>Y?$KodzNy7#W6Z=^`k%PZ zPVvpy{>9p1uoAjFXo_57m6P)tj7pw&; zFAgt!D|n?3#@kW{?0WCvo!X@LThOJo&oOYoMH`9lM!RZ?M>i#Y7c`@0**J)mxr;K@ z9`TC3*ARYbgO07^@2m0ygVr8{lnMWMRW9(_effT%5}PI_%r6oHrie?&SPy#@Mij^N@+L-7to6 zOt3qF^<{hm3z-{olE=3uXq!_nT6>?@0mW~{@mvUOE$>Fdt}l~%mFtTdG}pE8b01$-mjL2yigPO`yh9pno1Ec(M-_`)vx zE9fLWZw<{6KPj)ywiGX2h#upP@zMp<8@e-*my)`R;3atHMfOHfj?3mH%53k@{)JWe zTyn6O8#)Hy8I>zOK{vGd<|6ORC&i}WPd}RZpd}SBk>0I*DJDf|3n9pT8GAb`{q*st@63?Y2GQ3yK;@v(Yw}LNoiXn-^q2FQQpna8QVyu!w}Cr z(wZBDH

C2HKZV!8fnN&mNPSCjMj2+HfEEu4Nr)puVtlF;{*FA2Ql#^GOTyQ+OmD z`)__Lds>j=%waPA1Ya(*#x=-aFCP0j?sFlW5VI&Yc0a!@6E>meKh|U+fbmu zf&XrF-%^jBr$*%l?KyBEdR%RRf6zkjtDvvePW2D!4*c+^X}ExXud*y_-GNh-o5wf5 zpp4ux#D&z@OCOTovwXAG6H`%qB)xnz>(OoCU^6kyM$7lbnbfU0-M#RV3mJ2acSF2$ zL!VdBVfR4q2>R|m^xO`v9AYg$kDeRh@|d*BoMz7Q2DfbgsdNlI^!(CO5A9PQFQ9){ zoNC#Z<7EFS{o76d&LOs5zB9MskKodV-4Qx~ck6~}rc5vfoMupPt}HZVpS)UnavQ!~kCx+$HjlZF{OaKSt-yZ! z$)TOX4g0hX46JHA*@q455_CS*-$fnTpM@=M=g_LslLlPo6tYI(H1_;$vvWejwd<^m zKjJ4Gr;}vC_xY{{dZ>&C^dCCu|M&Rc3=Ny1>xby_8V}}?7GMogy#L*QCuSUQB#sAf z{R*E+WUR)6v&oS@du)Cl`}CK?|7Tz*`?rHbJCKbBj@`EMl8|$HAgm06QQrjP841vemf4pjW z<5uLrk5{=HdyxSVWI%v=e#qG*YRgyPk0jt|gVxD>n+)$Xv$tt0{OLp1Y-ViwDfb&` z+jp7Qx364Y+H55^3?RD<`%XeDr-5?(tkpK ztOM_D_4TF6oMT=`dcb}LcG8kaQ{G9)O{I^npI#~&Uii*X^)YDv*oQ+q(GkPToTC~> zHzvAv5W<%2LI*gSgr1H5dKWrCFZ%0p^Z<<`%sr~Hw_hCM z-jDvOG=mO`uh+%BvVZf9@-HLZO&;mG%&F|WN(0ZfuB&pEF*dxV^0;3}dVmu|PJ*jxx>fFldfyo~9 zz?qc)0`F$>t#rVGm<}jg`GDqM=zuxIkI856jbJo?MGw?G$QPSC6<9M%oal8rXU_N{ zH){?R0H2yiojHfv)gPTh{cpZ~JofG9&)LR(W-itB|DnE(pZBfrzi#JUd^^1Mz#J~# z-db=aJy|$%fS(swlMFCl!9_A*P87BMkIW}n&qQW@Ggc<@Cc1`2xdHsplHk?5=vT>g z6+N#fo8qmMMZa0CxuNO;U)7q^h18$4Rw_MN^bWbgW#UJhcB5y7(8U!GMtN)C51aRy z6V-Cg?mRAwIhD^i(>(CG)}_?9RF34e$DI_epsYZ$e=vXTH~-wB?>RFhFd?7CuqKyuja3rv=;)$e&vUnKTbbPUuO6}81Yy-j^ea~*4o&K?_M z&vRtw`1JN3+Xr8D&SBq>J!g{tC-bNfYmk~tpSItMF}-Gt=GMe!8E58bUwdSO6)uDZ z2dUpzN}LVmm2}ow0p^n8B?~)C3-9%fVUDi3wq>l*<)&hF2lIZ}VuGt!Yya7cEv3GX#X4x?E zkyEJ?v1g24-OE}=ekd9H5$g!9iHs4B)u9sRy>E^V*IrBP8Dj=(Zzk@=-APT6wE7ak zDHx5rw8ZE6MTyEbRxA4s*k8^1g_}KEc5EDUp%81Zj5#M-md4g|KQjk;*7n^AfYZn{ zGgo~J#iAOA4*3AOcEcsu~V zE(CT%%v!~063iXHMUMmKmQkg}<+P>m3-C$ICpOSc9-i}&Po{`FuttixYprLlsUGfU zy(!_$L$p2iT_5{fg8nNwPZgLOCuP1>m2F=1arUg-#k1Nh9cZg7 zsl8V7OxkW(U+~F(fG%5Zl5GFzu@bLvs5uTsoYQugIG%4)ANUq#U9LhGXAeWzOEAc4XxPn}ME}iSt#ueo9b1h$y)Ks@B zsVVr4q^4Tzg0wx`F)+5I4j9yTH^Udf+J#NM@WN(n?QY%S4e|Q|>aOGJ#9pxu+WRjx zOKMdP<<(Md?;rl7BuHBD?~|K?*K%Ez-1IJbsCe}MybO8GQT!Ju zMTUTr4)cp*y?;RO-x!*!w3Siry8ilPxGm;KfmADL^f8k(dJk7BgXt)2BBSA%G-wVz!=j(~>bnwTJhS%PJP-S6YFw##dgozJ343F*EXt0Tq5Ph;=fv`*oSu*R zPtP|dmM{7Ae83p>XXES;a5!Ib2)U*)h5b3%k`*=&A;-@q!yUE^cOb(Z4c397>e{Ck zmn{cRvUhDUj`mL4Dw?&@F43=*wuzRJ{hNzJ`=R6hA!4BrBMiJ$U|Sh*dc5&Ddo%j} z72rqokxXg?o(TKri=dnAwjSyEjD?YBhvo|2h`jBL7IeoQeFY`BG z2m(VbdGDfMsJ<8+z~_(j?b!ht9VmyL{{omt60~POE;#9J)I2 zkN%#2!uL`!mu;N${C^ogL<8ac3C0(bW9BwKzqst%2Nsuo>s5^@le|AAy`S_Z(r+Vu z1?h8%i=vpflC#MBD)l?0)eC4}{mm7FDfB%Tyy<{1Q&!$ksy$dYkbYD$mU1LtTP0H|?~l-BJo>=5;GyxC zrq5o@v)cJ>+BwWyJg=jj;;ogUeW3?Fw%Ot<#nu+>8jPEXEuY2^nWWGx|`*+awTcQWD!lbulN@tXQdK2wZnx`?x z9zPp+_NQ}RV!AW!dhn%vfWcH}T0HG)(rRw57z_Y+Vt*}#k4k7mF+4Pp|GE56=6@x1 z2H*kqj(9WRmmJ0v$r(ML!*jNtp^2VH@jQX&(&29=d0nJU9R6mK*YKP>{0;FAkYmX_ zU(7e6&ne0&MrXa0%dK+Y(*wxF$UhCuy#(0*`4{q`8?obPPE(r}C~@EK2F~%r#ogH;dhz%dEJu?v18w8N3!^EVt7dCub_H zAZ2DWPvd!+2G}G+TCj-)UO{hT-BWc*Uym?VUa&7IyujWB{Xlm83T1i#%eSglG2?Mpg_xt<)UcW!)b>^IX&faUUwf5R; zt-bczdS7tp{U34f^wWH3KaaD9Ltcty0qp8C`+X+e4880`Jc zTo(q)Bm9pn7SCJL8(j|1N0vA|zm+}7;sr0fz?$6f9_5$ChK?m{XsgjmiHT0w&A@Kn ztuC#x+jj7K#XAmeYyLXV?)SBn)uC5bV?Xr)m)e!>va$Hnhi_o6s7Hpb{7S{}swU=u z%@xBdei!Uob(w>^55sw*&gZ(StTgG<>id*gzKOoo(@rH`112?b0H#dNv z0%WE3;)jc#%WZxse4>W@N(J0o9^x{MDPPH6ZbOc>3gX0q>=CiBKP#|DEo@Ku^u$|X z`aO?+)v-oc`^So5_0J0(8`Ig3bCb1OV*4 z#Jo9YhP6B#FweJcFmHz6pMO|-VcnlM93JzIS*~?f#YI`ruP^fQ6+8FI{AQ!%yZMn{HQKa2^CsPgw`X2(+4k%UBHTCfe+~b?v_1Q#LT~tEK5uxn z+gtlFr2apR69K7L_-hTq2Tb}sSp3-dGlf_$^lQ*@<|UznfaSGalm5^HnYlEIA~ zO9C5XOPY#m&dM*USyIE*;u zHMH+Fu9kV=Z2-Q=f4|!H!?^yO$NHifYKAxTuG+QG0~+CtAhF_xcwos}Vh(NI2p#%r ziPqGbX+v_dfp%ECjgs$kx%j2hpIN%H_KNbl&y0)u82>xNR+L!wsvZBz`77sTmuzIr z7ohjdr|+KM`u4W{Yk;^htNzo=EpU}rKb~s?wwk_GT*db4K$qt08<%K3%~StTt_{d3 zyBvHfcbv+9d4*H{b*_rrR(UV|+>Sl2^6{T`%Jp85_)4(9=DJ@H)2uy{*_Uu8X6?G# zl$O(t>$y%641yS=^V+A!5FU9eub(i}1$`MZ*NGl*=~Se85uli#`TlpyO1h!5d;Y8zvuIY!7-+o{x~v2qt?4}jOc^Ev{NEC0;(Iq_`9kPqA)a~H);CVMk@&%=8G=x)z}9C%wY(d=_I-paeF}V>D}9Q^o%vsS)<4p( ze0!h;xMI1}u`j5naX0t`chdD-!0rQYQ-SGw*mGXiD?;pTkL?*j7j*QbZvoRT$~5-G zlhPx)(f^C6NAPU~=iWz+4P(!bxPs%z4~#8OEXjSwwS~-Yv}L}>mihNk-;i3UGkyY70%OmJ6zO8`AYt{5UBR>c@0XS6j6lI- z#<^SE9``}zj1kR`Bx!!dKcG3hIJ*bh!=JMs;o8GB#`O`dcXRy#*Siut0e{*&G42#k zM3Owgo?DwIh*3}SM9ufVS#seSbbppJLp1KZyDli1L7Dis4t%Rmjo^19c#rLI`WAZ0 zUMn~x`uMzKqfcPh>f6wc-;`X)*wp_I(+T@KgkN$O{009mzKn_=O)U}c$Y&Gl%qx-n zT=|(R?EMinz`qH(AvwATpSNV`&T7-eJWT#Z{v%^6Uz}Q6ZPM$>PwT%Cx%V{AND^C4YJ3M_J;^Q*<>k}vW*S;(83UvrP_kl6*{ga)i7hz3DuaKmPY23gP`xM7@41FhGDIh(8|^HksRQ~IWSTgkrV zGf!7=uQ^Hl@^VVw`dG)C{YSIA<`t(81N7k+@Vq}WzU@!!|CvG`R8NqfaNPS+%?Tg5 za>Z}5B~)MUOSL?^cAa?6eg-{yu`hg|yKbHM&VB|?@eR}Wu|uJ|wHG8%ZqdK+XBMt6 z@_Mr5KWy+8)e9e)&{6cQAU^JLZ$?)AH4E1__~z925^uQ5o0(PrMd!YP`~TB1knDSq zF;#tuS(AC!$nvL%OW1hml4uinszWw6?Q~?bY^JZzFw1VjUh{K(c+PXVi}{g@*0!To z^GrPS+HY?>@hhIc`P=JH_`%(muJ!bvx2JjVL_g&6B>qb6FAM1IPc)L4BirbJE-N-J@73cqCWO)V8 zzN=^CneD%Qr1(4huffLdeF{2aXE$Ifq<-3uhq$S13Qb9P&@hGgqzYWA4o}i`F!* zb#zR7eu#{THZVUV{W`Vnv9D>=No!Uwq6jbqmo+!P&}t@R2ar z-O1d!2tQ>Ww2wUJ=%?NTT$!I@Pb;1*!!KW3Gx}O&A%0SDp7uw4VeA)0r+xkyJfQt< zcmCWI9|b<>^!!oxxpAK{?6b>g$zgo=vG(HCct+kVi?`^{O6J~l;1Ul@=EiQ&d`_GwGRH+fy0M}9fwK#P0Rw;K#n?)cOkYYOea< zlvro^M{t=3;g#<&#`idI?E^ZQ z7?Xf?&ZTCVV4Zz?`t<$4JA0aI`hMQo?_HR+A6X}O_XF?dRVx)MTb@S0YtaK%5Eq+& zyUSkB$>_QC(Jmj8L3L0dm6q2d>yt zdB!uOF|6^lpS}7|B1hXLdL!VJ{sV~ zwBLsI26|d^H+rqCP0YJ(#0m5lnzC1EgT0tTTFaH5eAD01wW+Tk8L%C{0BgoAzM`jc zM~r*Omrab*75FfbD=SRK!Ij2!FowTOexsMm@E`F!9pBL^VgU!-o~l{nJWGxftIvM! z7b^Jg#V>@t3y)?Fx$p@Yd_(i`3%U8p&tv54ncub~%rD5#@Y^_J){-#4AV0%zGd`Y$ z_;_l$y14e>>%rf%#GBT5Nhag#Ws^2^6kNnuI}x0H;DP=R8Tz2OvOaj^0P7ch;AMdE za#|U@aGc!EeelBoWBjx-Xg)?|@aq6N*J)+QuQ~X3`_MB6a?U72-d(QvE%c88^p(@T zLzh{FU%C%DK0v;M)5?(NTNMw6PCbC0eOehZe-|-OeO}56&nWZKzc(o>q^#(SvO@az z24zK*oqtAI5&cuy`IKFFM%nrF?{&&9q^$UivJ2@S{_4JB%F50tE2e+MKJ}Hceh&T{ z!GC`laveT3_#VyvP3PdM}{fUgSSt5AZkS`&Qbfv=9^Yic{H&9GVY8^U>$fdyJk#??LE2`W#wcPVUtKXgvt6N1sFIt(+-60G$V+ zvz{B^?}Nq%$j3SWjn&^l=sfxjG(L_Gc>o#@LgUfrqOYDq-$Cd*`W)J>dYR|Yb`aW* zK8LQm{)jFKU5%$=Y+r(=A!sW9^s6Qfnx+jkou~ORvQ%R*A6ex2ZOvZzxV{28r198? z9jo_}ddpRO-_4xu{WkJr&4?Z6HU~R(&ZqNR>a8&?UTEdBag$|_=b}h;*(`Lh&`Y5c zoz!=3thy}!5MD*>1n=E>WkHR3^l5J-58s8}TXDi*S9l-EE3==+^2)Bm)-a!6xz2ki z@eG}<7dt_FSEnZ6iET!wz0rYZ2Yk1Gk>E*p;IVnZfoB$N)B;Q3b1P1-580~v+{yYj z(ZB0C*9HCQ0Qec8&M}$0wvG1D=9fet>C-IwG}=enkU!JzBmEfdBYm0Om(s_S{*3nV zjYJ>&PWADRCq}03cmVwZJ=X_3(l3&_#n^?C!KNTFgI9ows} zcH#PbuRE*Nn{VeVIMY^@|8DT#v2WLo6wjh9`89}%vvnBFt+nV|F59<}G1TL8bSu@_ zYOH$kNbSJl@)Oii`EBeH_+t~eOXqi-IrvzAqrJ|hIY`gZohQ7^cac2hNWhM#p83ps z%J0x>GU_8`#D|jO;an@NDr3$wx%fcF)}h~R+~8TZ(ff2Rx~X4!kot14A>L_|LH_Rr229s`|iB!v8O5sKCJ9ZZ_!|>!DXdgy*Ot)NB zM~e36K22<5m|Vr3TdegIKxcX%>P&29{;`#m4`dAZTg5Sl)fYRg^o9I%BI81K)|!zUsf{NT2{L4o0J}FJQ?=7Kmgve^;p)mWK(vMuP14{*Sz6k+N$r0aQ`CNis}E{fB4svAL>7pO*W7Htv&Qt zJ~kJ2nQW|$_<1EeHk!1ojh8?(&WrQmL#*()ZQp1CzEL;hSpFO|-rtcZM8@SVKxHEx!yRoV=fIE}e^)NPgrj?O31{ow-l!=Ww zhv(<&88WDdv!RaCr*vdfrY|r-Wk*??Z{~a+`G03y=~?nq%MO*VI<=p@yr(!^;i7ql zd(@xpMo+YHfgY(i%EknqyNY5oE{6`nY`dlUKH#j6iNL-FZRXed6Fd`sHT;!CX; z%8nGz`Z#03SM>LM`Wfn6z+9u4`dRqOw=>UZZrRA3qq$`j>k*m<>nKmog_;97Gt8a? z=V=Wkh0l{buKZwa?5h%Qf9qY&yDf2iR<+26g^9ISFXKmkpn1rYKCQQcha~TAhgY}! z{^Z}Sfoklz>6gW~+Oyz7wzyaab-_>abL3f^TMa+Tf7VSMEB#JRyL`2q$Z2P{i*IVQ zUA|4WyP0pE{f*OZ0pDCVKe6^odoI>CN83Tx>KnDw$v3kmn!k&ykq&Tb|@XtPeqcSFYlPzP= zkzODM<%I>@OV;#1hnxXc$(dU582U;Nx~##69Rd#N%Z}_xv(G!0>?uNKTZwsL7P_Nn3*&{@T?NbqZqd!24?OmK5J<>I^C5FU#ldfsTmX(G%iL$g%M`;~ia)8e<2GDF z0s5^ot5V}OLd0!EkjskOxQF+9@xdqky^kGoV#~bvg*PBCt;BkFCot~-X7Rwv1m6^h z=YYEaKCpT3kk&NQhdlUaeDH$$a5->F*K&a`(Q^fRtfep6`)b5z@*6VF7fL7Ly`&#; z8uw%QejzZ_O#grHH|u*AnFl|iYuNik0>CBNEPs{cv*ep(bRjZXey?<3WUNn^h2O6- z8~MB;eY$*qVal|BB<$6iTjnIm>6OT?1v!*c*6Cf4)yeZT@gXuRh|H?wx!$kiIdMki zzlZ0n$xdHZ!c+1A?w{|>Hy`GkH2Z)Z*eY~B4SqktZtHB+nSD`eq2NS zlC2s?=%3STJY%M7T->J}9q6#q+aD1eV_ zA0K1Y_VK|>`S8&^)_MrBQ{SCiYM{=OSc;3Ny$a(DmcWI zx(a%@_HccK>myu$z%|C0yPLI#yW~$yAM!CCeDJG$`jLh7FA{@L#$a zeyXW`56>)l^RMtHa3Bk=9)PA#Q$8!R6F<4upusgV&-Q=shL(wOpfyLWy}?ge@_RRU zefHX$g&dTQUjrQT8GyC;#;d)Bwx7JlYh~5q8?WZw8u`Zm57(a+LpXpQe+)R~;~z`D zL41$s@%iLoRIb4>EiUxvRH+YPEgVpHlLm5rEx*GET8EmGp*4J#E-b>!U zfs}lMKSRz9B-*IjP;vNr+E9K+wb5s0F1wR?@6Xs^sd*mr(AP7w$x%3gSVR8j(?+1E z$IdYbuTBimMjtRKuVW*)=};T!@yWhqkxx?Xs4vO3TD8A0 zdlUUpK0||UueJtK+S;PFj-0gpEAZ?KVcV{Ax8y@hgMRJBFFem=*)m&xix=M}cKlW6 zeR$;s#`)5PW(WR?bmG~NJGP%JYbb41h&UnnE8ug<(N%S}pWCkUjl8lp`lW#&MrEPTU^iJ|O(--~>gpIUW1LLE=` zog}BD6(2`EyU2NS*96rwu6|Zp?nH0ejES?;%)}pU@JyPMHgn=L*xP4qa7{WoE#3e6 zm<9e{jWK@zmuD_RFZRD~rTKqFPPxSs#`uqBru&bav%vrKgn&O_jq#hz%l#P#t}RPH zu(B-ezzQ3l{dKt$J2$wN?Vom;ztaQE_zbX}{l+tMV$6`6ZZdGPmb_^pYeLYz2Y<8t zTK&H}xx5lS3QJ$GQt(Z`Qt`cB`1!4aZ|U%({wCo%;cv2WjU1T8_|KPI5U#xd^KD-18?Bd?CQ$q&qb&KPWYh`ppoZN5G?UP)QNq2=@N%&GM{^ff&neTMAT z+MMjiY9pT`?`pk{Il_WAqjJXW*m(hHvpFGWLj0GUxd51I22a}ZrAhq9^McF{u!%kjxv1#bR3pA7ou)tZOE-KqBQfAzj}Mth8vn`erbvVgOe`N(yJJ%10QzfK5xD-UZOL@~6xl-q@vZS+EW9cn!1 z@F<4+0Q)blg}{otr^9qeRnYJ@d}PT+h3mUp)~$K32!l1l^@6VyhG)Ta~FDvUCw{IoUwQu zxnh^I*5G1pwafW$m$T+@oOoxuoH^RXyla>9-!5nE;W+DJb~$SgE^GI$6XpE3%L^%IEzB-IpYoza zIsfhQBFb40v&%1}{QN{Y|LyYgDQ8X0E@z#>bz!2M|8_a+6~|c@>jZzkGRlip&VG3Q zw}D@ab&KPyjnz_av35~mtSpPQ3(-}2mW$`|We1Hr%ke{}+xDNz(ILspZ*~_3$iu{s z`D*AD_75m-yN_!GxzE{K#SQS_*X8UN*a2TP!Bf(EN56x&RSxvzG>PfhTWW%E^{ zPIzh;@gzIor6%}D_UY(%@Y4bI;_ZNsn&2VJ=BLDW;wAQL?0|=w;2+E8r9>HgG)86c zPZPXj*?g2JgNNp@zPtn8X@YMon}-r*@XzJQg&pus6Fg(t{F5kycUCd>cfd1E@QY>h zPNEFH*@{ij4!>-KS1g-vJVQ2rXswH}-wwZ=$ukCeE@19czCU~_S%rx_a(T>)nybV& zo-6P%!8`a_CPeVjxXJ5<>^#_)ZHoUHFNJa)UYbKpe_eu?Ho{BHEwXWJUW&j=2Dx>L zmpntWfXQA9MUIK5+`!QQj};mNPZ^u176NZ3beDLjR~8{KO2RN}i7p2~!$w%R&q)5H4ZX^JHRX~+yyKS+3eB(YW9*F} zk9Kh#Oz@EQHHnAN2W=h-!9%VD4@HnmlJ`#|mnwNvy;e z)VuQ&?``+YA04OQ$R4Nd=qIlrtJ!xxya4%snZ^e2L~;W7nbNZ6Ssru_UDExh|A*8;}0WJQqc_3tBQU_+kZt~_o04pr9O_ht>=%OYi|6@bo0TVF0gL=bE)gbG~`JqvZNDT z=sfTxyP+N3=X_wk!5VjPE4s|QOU;{G&*My8c%_oxskzEMGzgD0!z0c3Oz*nXnWOId zz?q}&;#)Vk+W6Xr!#(hTc%~yf()vY3`&+IWY95W&kgX$!Ml4S`QbPU;Z#7miEP_ir`gN`A4jucWR8I&wb$e?4$o+HSh zI?149SwaRKL-rg&2GvOhCCd^r=oqr+2r{US>u6a*1|38896<)vNd_gqOUR&O$ets} zpgPH*WLZK6y@LD+^Mfa{25lKM1{p;B9%Es#m3HuYYivukWepvlWR|nee$b6Pl3ZG0 z(hgSfUL`!(fIt3x{!1>+GY*fn@=W_I=ksnivPyF4Ra;(phV))FV^8{1A9iz?z0Xgv z*5HL78jR=q05ZYDe}gXHDxF^OzU)y<>QmxTL%H;+@yM^$*wS10U1r?Kke*8q5ND=2 zF^y{;@@z=F3>+83zlEKdH~j$qUD=s=!L|G^=K5ovf6Lyhn0?dzJF+im>dd}rkXW?) zc4XgF&iwU~C3o@8$M|Ja$JU!Vm+UY8`I3jZ?=1e=l8wbbU7|5~S>F29^>ZItQa7V^ z^}?BJR=el^V9DaSKU|WQ_tn*N=03b6GGlOc5%AtXAD)@{{nfR=y?JJQb?3}ot9|t4 z&P@+339{!hZ4-Mg*+)5;7{l!!GKP<#m$#bnLq0Pe`8~cRge;s-&W&^VEkq^`8c$Ub z^00^N3gn*~ellFY|Nf?5gt@NxVAH`Y^3{eo&#E3730>;L$jJRYcjoH9Ikbr@`;B`x z=W4tc;u8_yY2G+?ZU7k=Kn7MI1A{jr7o(Doi!JNm7;ZV zot;45-lsh5!DlaD!+)6Jh9;}4;TK#VB#)*mvqWcPe(|A`b`BXsj{2|MY;{e>W>yY6 z&~J&q-&C z$TlaJy1)LXBM&I{L}!b2@co?|rY+md`sF6phVU!k$C-e=!TxA+Q9m?j%6A-Poqx!b zZ-3pC|LK5Y?(KTwpFT)FU*J7JUH?wIsks*?Eedi*DL5gggYw6}MP5*(?;`6n7Fy=vSHz5&;)&D zsf*uFJFMvaTqodjf2$$z6MP~Q^US0nU?Xp`@vA?RxNqe9KJs1nx>|F8zT>2wPbq?b zsi{f(-tf6SSiu<#R#qczg@WFeJs*thnG}hn_s@M7dhKQ?=|wxT9^5LFSb&n za-RUFWym4sHngF0GiLaYKOtJq@K2-P?WMtC%kA)4Bi|(X>|M^;PUh_X;I{oc!Jj>5 zgS@-UOggxXJk#Q(R$v(5D%^YNALju?8-Hzv72`j}<_%}OXpBq-=YPe|F8p&|IA;pD z<88|GLH^817dm`{4|}*KopX2?KkUE$aWA>WMH|tf2$|L3vW7a)hu6BSgPd9TV~b~v zOM`ab&LRJ*m-9`u7orzf4CnAEpT(n$r8dTrzV(g&M}2h0)g;z!l642*sbt-6xy&&7 z=mXvlM&|y^?TR*WR_-+Sl&WEJ?j+y&_LX^igXSFkkiTNy))2fuQ&)$pttx`enM<~{-+eFk3WK{mVx>^5J!r?<3SY=%F<+}Q8V zjgMzcD6c^uv==sK9pqW;;=u3-I{#nc;YQ$6`JIf{2N|z-QGe{K zoSW!2^#)(nsWCe-y6+bw5B!wxx6p6ryA1NJofAL7z51!V%^q}0`FP}8+jt4F7wD0Z z5ObArWf`w^p1G>^8p)L8xf-*0zmWIydB2nQ3wXaRIP=Jo>UpC zv7#}ObKm0f)|qC_0qRH{m&r5V@1JRwO(Osa>4 z%^ME_r^Abr?PsUvihVop+LOoy&-`)fY-dNNONJ!zmXsHguguHw6WGg=HGgw^{FBKE zJUtRw`u1%NKN&_Qhme!66s`oSobiSl@ zck4B}y6*lCYmTmieaMFzXD2V9i%JV(dEeMRQjjNW$|E=W&`f&{)ZUnk{_y5b7LybllqFeD%jPC zT=XGp+otN=#$zS+SyP^A%unOiM3AXI@*An%njM}l@kLuR`Zu)cXvW`hC;7s@3GXr{ z+FH0WCc>MQpV%IQSq;Y2_p$#;x=j1jQ+iA3YzWRbGy!>#8bvK8%$$b>aiH<=xV(we| zYt1!zLCy_w=9;!df1NS0Z*&f|>nd7&Sg{bP_1sK7m($nLT;qvc)>c{2&V^Njj&TX= zD>^qlbP@A5>m-^Zm8*IuZRMdWaz^iRow4pCNB@1y9ipN5@e%qX7#;jS>Ett`Z?%l! zH1t5v2IAn*16Q<9D6iWwp*$R)P#*f>gz|=;OenAUlxw)wHL?5_cv@{s->~ZimJs*o zGVvZ@)3X6!NvAF81jWQW2{t{yn`cg6+2bF{i7ukA26&W{V`QI&Zf5QJpwdL&`B!&B zPq5-vo#cyaR|k02SPsLl9h`CN%QB_a#q15l7rwhd`LoSM%75jqv$~FQCb9afHrLXo zp6mYxo@?!}aUy$)xR*}(Ri1@PTwR@)Rg^3D_x8$)@`tJ_>^0xED=W%(EUPG&zFB*b ztE;YQZ28KY$pw1H*z%gC+2z*;!4E#}Zr1C27%$!I$JBihZ41xWePb*!R~~!cYv^lZ z?R~FXc<&fvDd3IS`Q<|S7IUL?YWD5Ar^G)x!Q5x(-@1VL86KL4ja{L-E6ww7Eej&w zDj7Q!yTF&Ni$!Q>%RgxA0Je_y*eb`HVmBA@ULf(V=J)IPA4=T+n)~l>x7YCw`9hPU zhn{t4sY{(BqJ;E`P6RBs~tANsddlNDj9>ZVgYGU`p=I z&MbX2E4y^^hQ;M;U3nKxf>x8qWtOnszh)=v7NLLn*3!Vg?73O+n)gG8$)%+HCt20V zk~Vbi-^IVSw3~VAiFo|xYc_mh+chJh!`C!4RfSk9k(~WBdR>4sJ9@$IdiwR#W-Ge( zY!ltnJU)7kr)%4K_c<-eab8RLTF!wX$KwO-E_^DkbB5Z|k!xewN7$ZMR`Zj*ve1)x zc8qXER&JI3oy*F4nfIMKrorda(eHBow@x(oMJ^(jA7?MNUX-?e9cMTv$8_FhhBfD@ z(XWF;lf_x-=@*v9Oi@X)+!vb?-AtL}M%&{R!#xR__p=`Jck=!`&3V$p$Jw;!+!e-E zPW%aAijez4b&7U*mQa9%!`G3qiqisUdRXiVr<|@+}-lJtblcTkiX-ux; zOjf}h`rElB>X-DoM)K8q!JYB;=2kOTUUN8-+s56^^MM=r&bbI0`;r9(#yz$nhrC4CDFvR7Fy>u; zjrn%wuyyo9b4?R6$~l{0pDp7wzEpqmnQ%1cSsUg@#NU%?GgbGZtvB4XB|f+9SoDWF z=%cna!qeN48&8FDb6cJTCn3*r;(zQ(^5*3DGmk^} zQ5;W>{^6OC2OjMZUQg*t^zDszr(>hjPw7a)Z?nd5ik$5qZ^-$cce8BXuOQ!h#|F9@ z$7Ys(8Gh4PG>pA3e}mk<@I|$mTwlX`!bcI;Hs~zeE7$GgS6bzdS6Sty;N;)&nU7x9<+Dt@{LT)&aPz5NVcXg}8s-^KIxbCYLF zxctxYo#e9WIN;QAxBWh2qLFu%mtrb0fAKTwx|On*c&2{X@Y9zVc(D6Jztx}T65nV{ z{TuHW+qB<3>bFYeamjG;w{-BN-Yh;B?{BY`F3K1?+u`>{@p}Zi!^h;CNc7v`_vN%> z^LzCvesBASw)S)O*lbCy1Kd?ohhjG$3v<>EJpT6kr+7cYyC%W=4LoPg^!G5Ax7};H z+OThL<(cwO_vD-C&mJ&cU*Vo}viv<4ndnb%BL^w>f75-?M4xCdU8}ht*8Nfw?P@k% z*K>bD_q8Uv_fFGA?n66Ac+bry`gn`!s^vcRO|`|m%y~tFQ(JReGt9^Pxi)Ye;95g2 z!fO6ka8>!dl;?Sg=X!4>-FZj<^{#~-fREtgJ@9mdoaa67G?W^bx5O2gfQ*}r{*n`K zA_w~OkJ&hfPrx~R^6q~0@lpN|9i~SA7tg-OGt11lsD03iHu9a`Z$wTMAVa>^eM(OJ zociuf;guKoc31o9{=89M!Cp(sL-B-oCMge_Ij3e+9^&heJj_X@Eo0z0>ONB*`p}o` zab??cPuVTnS=oPwuT2iV9GM)O z8vQGlHFmzi`zyf5NOR6<`VV=)-;kZHvGe%FuIN5+ zA^+6-2{|e~Rl1PI(}2r#b#%LiYi2mIv_dk{Yu4yoP32y-iaAGg?7Gr6cXr96v@6`M zZJ&zloE+arAKU4l{$mrygYZK;^t@7Zv-NE~pA`QxaN0UVcW_jPs5mD@W}WHFYtD&E zepLsoHG3IrTk$XFv)8!aJq;V&<)0!vyT}te-@3XF__1lr18Jt%WDr{c?k{F{G#h%2#5YIjb({G6uh*0KP5VeGz9=i1=3sQkw2XAA8J z*Xo~SXC?jkm0dP1{?2;|7)(x;U=ZE(%^iHxXrJGsZ$2I^;hSE{=9O7%E=T7bZO1h& z`lG}*Yxrg_|E=q{U>8h|*AzW`^I3mmJTvZoTIq_mr-yi za-VBb)q4D#_~7ef_yKmy55O4ZyGPg-7>(iQeDv3W;conR{q#*^N%#7Xy)!j7m==9> zpBq`mxr3bL*KPZGC&wR^&lLa5AUY(z!lnO7Uqkp+WB7Gz(MMzOW*B``-_@PP`W|-n zE$F70}4ccvnLupyB@?7n8B4fke^sEkKp+~smOhPa5 zRN_DR=+0)GbhHGzBFtHB;BzfGe`4TtE%?KZsShz1q~cBe^>Xfdg|CfuO`E^o{jubf#%8X6JASVSxPqT}Pj*AD|0&wMk9n&(PiJ0CjyIt%tBx=- z`(Dm@4B}7g#$Vc;$2sL$rfV7W68ss!F1!WM&FRx2yH756w1(%Ya)PqEld@v%lOKy# zCcb}-ePC^z2dOrbJSd(N9~J`pN9pH}sY|#Of9hS8f&Zx9i@~?zSACz)yWiwpwPnLW zJM*|7&FPI^iHv+B>FpP42 z5dQ=_7D@uH>=Nb4m;Aq-wjEkA=OH)dLaP`uSaY*M_S$wcF%_da8}jdi+n!0-137W6 zDTvRP4%>5n+&f;hG0?`T|JU$$a-ONBEidy-UR6n%ksKsOun#&C3#WZsb6-YBb>WX+ zg)gEP*`~bX|HSZl7-EWchVh$Q*t*mWuf32#UF`)H@w+Bx1@c0B zI#;6qXiul-x`K6e$cz=p4?WkjLXTNrH<4#kQl4egBWK;dmA=&>yOfhnZLdT(s{?6i#C5NFPLFImLI@JoxN6O zNhE*4`flth<=2UErg7f;?ln7^D<20xYEL+loi>ejzlE(o7k)~@(w+(nW9=GX(Rq6+ z-Zf#<)xZ$a zng?a_LnQloU20!?a-&}QBHg+f-;RSLXU$|I`R3j_+uZkE%EX7#6Ft29`&Q}AKTYL} zL$;oq8XKzA&_Q03>jN3kYNpMEs$qwLZ6MVp_x@0?a>p#!BDbuqTcs44p0@I^k16L35 z>`M40$v?O7Y@{+18{=m-|2RGwb4Y$D*%+ETlm1olln8>K{EPBM(SB4m!B?SsKXmtT25BR5BY+>4b%pviZ2mms zS#up%=vT~I{?(5{?>*_*%kDAJFQ-pV_*bW~?#vk>z!7s_Ufz~*xjhy&4nwoex(3%| zJO4$JH@?Pu2HtSS*wlDCb+U?d-(hf+{TTFMz(!> zYV3VF^;>9^#df&fF&( zEXkX{r2Xw5g1>(z+xo|ziNC<#;cr|j4xs5%v~wo@EatD|9G5D)J^%-)Jido-llc29 zWvTq|!vy{wOW;p_r7x!7@K5{?fWtO$*y}1QeTs4aq{ck>+J`)73|U$E%tt{lV;{V3 z2d~1P%G-E91|IbezG?7hY+O3LJURXjvPyg^S)w{+$MsNW4eu+4AfNXK+^(wNI^~#g zRVkh(S)Xz+7f?Rvc2|Wd*EOLx*>ldd^y^d5WyI||ZSCx@2_J;k&E5?Y(^Q{E>`c1t z%hp;ueu0vjF^(S6%Q&w&>#9=0Ta(aybH~nuc5<-=zm~*S!peO4WKXq0^S9NryvpHo%NySm=TyQkWp9DO*^zTlgj3IjU&4$A&JTx;$bt*>u7 z_`zS&JMw_=L`=if(LPb%J=|lPfO8Yib7$25nLTOXci6#=^t8$NM9%TQf~|eTJr7J;=R2#x49cI)+v;$Boh07#I-2~Qu9~d zf6m+s{h;Ht_$MDlKBI5ME)EQ1!wel~jO0T5C-naHp)z#&xowC zHe%o-(T~!0V$IR98~bKNi+Co!Ot$Ix$Igj&lUI8tZAKW&$u5EEh&{>oZA(qeu)y74sG-ICq6aMZq=h@!V{P2#R;xiw)NOd)?w7S|9TZ>+% z`o78hpsRRjI(6f-oZEr^&|Yapw`Ww?dWh^e(NTWG%DZN+>zH0q-tnF(@0iNe9mbL)_m1&Xe)j(wXr$9r{Q9CdMs# zDYiKN)1<%2i~PoB81*+9$#eWo-f{Sw7`L}94i3M>*t(B%3qwnS!+FT)B6R0uOo{wO zRW?4Hwank}KS0b)>-8mNO|B^|5zUF5MQGzI;V+UL2X_;Lp2&l3m8OgHxZ~}_QtZYi z?3T=vpBUcWM&C4c<@=FNthGV;Fcza5_t1tNCxY)ZmXHz5-=n&c;_#Aj;_8EemLce< zzWmql$R5=tU*zrh&7{Y9@lD=>zi}P$nFqjs1ON3-1NZ7{9zOQBc<%5r>zq9^qtE|` zvz8*i%y;OwBO|8x@8|#Md!d=pCD2JS;?%PV(VyctZM5eo)%CIH0^ZS@rSK#_eI|V$ zEr-_UQ~tqikEhy3k7)fx@>uH?PhwX~_7YQU`xtFHUv`Sl0oJKg>FniRv5O&W@ECM{ z92~ucpZi_t9D~jc$eArRoi$DshnVAk9@(n@;`@K2k1=SFL#%NhH10t@^)rtf$~?^5 z`Hbf$r3)}0Pr`@R;L54TN4s2Yko9TJBIHTvEUlrKdN*S)1YIP{h+ipIzkQKB_HmgU z`@fk6?I*|I2WPe&cUj=n8V~%M)Q=sSAF%Cv^+z;UOs3lWs!elet;j%g&6_b|FCT}_ zk3!@9;9UM@wbO0W96OBnk~BvbjXLWuishOd{|NmDwnO?a`n?Ej&bZC-PoYdcAgw1% za^8Exeh*$a$b0`uSqEz_UuCVuL#(yR^e+Bpf8AZvPu%jh6Q`_rtcc_%>nRl{Ms|1e zoz~CSjy2Kk^zVT{DfXtmofaRtKELc<)&aI_tVj;=UiUK7^(f_d@%18N|*d_>R51$d_sGoqTdR{)=tCn`-l2 zxBcJDT@K$Fe9?xhg)YgO&uM&ztn-Vfh)=coj&bF0_=L4)3w&qgi0`cWX86v6H?4Z| zF2Z;6zxN%@!GBZZUjQez%=zponPZ*dW4Gf{?Kq`_HXo{g;=?}pQ2jh{Lb6A5 z{G|97@tLvSZu8*@c<(Lv?n!7dV)NnD_=wGi)8gXSR6dmNQ820=ANxGw$RY8M?!O9t z9KNIszPtdK5A(i@{4}om+OquNk-77T!Jb5(p7*d3ByY8jxr=dRs@B@;m+;)&bjF^k zsv&o~DTaPFzP2!aiM!{2ZvAm+q;<_0x_KWurpBsO6)Hn-m#%J)YrTIC|L3s|+9$ij zOs{_y-AecE_(r#}zSXhM6?MjdaH27Ajy(n*eajgGUg*&e|66a5?9p1-eCQ0#Tk@_e zFPli4`sNe#S?9m%f2q^|1a4+VUk5k$Qg)x~oT}aQHOHOP(mU(J{KEC+W!u+k z4w@R@PW`ft+xID+GO~11<;5+zm7i!ihq$@DjGafXG{a9UGs9hsp>AaB_9`>{&=_-H z==D=$=_|A)e29*+MRYc%cwo2c=Km<&7-NqE+czFO6>H!*QDLu{OW)C4t#}jetG)8+$Z?I=;?BB!Tg6@+ypMm|vy)Xsafqjjhr%8R*|Je1xZqAuxLqM6>WrH^mB-TsrvIpL|FHP)Z;Zn7@&b3D+8 z{8OFsSBCyp*`@!2^?YC*g{#LKJ^b^OdD`oXUILuY^3CWwq5S9qo}Ie)Mk}~ieQGnG zeb(BCQF()PbpvsGAGld31BXG6b#?VCg_KN__W~-`Mg6dCVE^j7wra{M!%9-$q>FZvy2V#FDiC-tkxXLLNVJ zrQhdu`D>QCx|Z)6d7ugVM(--__zT?EKwI5+jS$-b-*`Dst&O%q#Mu+q=hxa{8+}O5 z>&ZC5ecy4$iuS<>e!;CiXzeNe!eE#CyNk=yF02?n#$NO>w=6E#{@Lc)rr2|dbKYwM zy8IY;d@V7-Ywy^xt-(bOJA5<83eE01@(JFXX>Mjduz7#7VEz)Zp7>Y#IA6KXWR}dE zY}V`qF27|aHnFx|?O~lsAqWBVP(D0nQdWuHpGAb`omk?AHJev+uDC=ci;xjIUt4lwW9GhGGcHmRoWR}ya(mcvfo~1+ zRQpr&KT@IkTU^G4{%_tek*uhDUF}}2S>@x;k*{hO*Py}rFk{9B!AHI^<*nrLXd?Gc zrsX-voD&uOLNmJZaCKovf{-i%G zyWLDrhbBenMlTXO@hH~;=BRzZ8#ZIA!ua;x-hH{;e9X)+Ay&jl1fv z$Rcyg9ryHetz8({%evQ_t_5aq8-40_r41e6T#7xew1b(JY3X)N8CvTyE$#HvK(88f zVr!MR41D7Mnr!>OZl>Q)B2Uqu{|@(azD1=YYq?z&%zBx07TrQkf zvBKqPu`Ex^9+&4}J3P_NcorY&KE^ti*JH|KtaEi5Pm4Fxl>4|o3tsc7UpNphgahFs zS^p}|sn`QP)`AOSZ{HL?^o`!tdz1L~ZN3#vA9v-jU)tpC;fns?f0=ciwXPg+nA0*P za{q(x+Rrm>T@O1so`L*2hV1Hgl&~j-0_;(fpsR+ylKrc>yo^6{0~Xqa=}|};B);`-ugZ9F{5qv z6-HOkj_giyx%iVZSMx+4_JA#W(f`d2&fa0g{~7FnnCwpU`NRA){=>u&Nd~M~0uCeY zXgA|)#!Iz(BMt89tZ{aES>xy?W;!s1E8{D~xQeX}bUnrxUeElFuj}gahR3#D!>^qE zoWDkv;ty|Od>6mVp1%lt9;So0z4)um7p88Dq30=fq2KnOO^NUN_Q?lY(fJ-`&U~9a zqsVsj1g-fi7HLYH^}7l8xu&+X@?N)VM!gSSm#%pYdM0%5x-G*@D0ffgeD3M>i|_-f zAA+wNxfmiYZx!;o6Ids2=-l?@bhT|BX~Ra%0WXn+Z=wP@IV$f@i$!$oRn@1u+PL;H z7U!DIZF`X6s!w~^RQ^@)@a`SK!#AS48~&G?sV%Mz!NaVhv>ccmDbYI*r{yj4<6Ft3 zkB0XS>PpT{H@U>m=Krtw{h7Re$Khw;WX_yl=8_v=;*In{u{5(8NBb;OHQ=`5&2#qU z&hqu-hD|ziR+^uj)%BI+@mEZY+l$ebVA-w&BzTjhc{xx!w4)cE<{ZL<~HB=sUafU&enbvY6xnb0&Pol$3^yziZOHkh~FlR%j z3Y$*H)0{r728X}0W-hy)e(}x(n_lE$9YVHrU{7Szr?0DDhgAYgt z?B2BzytXnYOC}XDuG2mj9G-mq@F4{ncQO_zllLsKG9)C&xu5qF3WCxu<@W7zMpv6)@@eT zv`FNIx7nW?Bj-Y-xuPq={*cDzV3&_rDa*#sjQ9r5d?bfKd@S+ek!Dv{qkqfNre*AF zE!uUn;&qAV^K-_!>>2!2Gw|!oioW^8$OGFt=6AUgb<9Jiee&*Yx&7>$z4CR(FXqIH z%>Z}P{r~j_e*SK9EYtS3W~)p4g4Nbs>{5Js^5ZV8y{GjW?Z?r56R^L>p5oi?+4f-7 z{U;x2+E#&WX8h{YJaDV|XRT}c!5I27KKHp7+kQE7cN;kvrh=coq`XA0{&0DDDi4F& zeh{3g?e9?k1+-`PnKspy+QjZ;F6W#QYw_Iba)Dd* zvj-b{E%BxQg55ZZyTI(|AHbcR0{~opKF(bHn*eRki2uvT$nK{o8-2HLR`hx9UxzR5 z2d3m+R_i+Ua%MTUz0aCf5`kVd#P&v@mtuAz$Y5uWma{jN{=ER5URZ^lq`WC1VtGR+ z^Ga(J4+`wcZK68Vmm2&_sde=-Pbue$>?}L)3bC^_O+nh%-km3RzV?RJS^mTJ@42qP zb}9>YRWQB zkJZ@vCb_q$=RD(yel*~To*(fo4BvH$dktJGJZSw$uCrMQLrma zSuHu34`A!~7~_f&SV^vA@;O!IXFi!*yV%v0?#?W&DG7F^^WM?X_tLYFqfwv9sPbj* z&#n1{i+mxbxE)%)g6^WW)PAx(!Lb#dsQZeO4 zV;hb)#VdfV7Fbr~5Mu}qgbV39>WkZ?MNJ?rT1-D`fhSC!Fy-gG&*jc)BK9?i9i{$+ zzJ{%8vP){UhYXzp`+TX1H08cJHdIneY+N;Rt}n+de{!Oko0ljL6O(6pnsQz1@|HCq zhXdIW&OBOF5~dFO8TLuw?nrKjnK1EHv(B!k{cOH3q<-SG4Ss;YBaz%R>q{lI)a9Zt zuZ|0q-K6o#4Xm^L^>| zdwz*+BwG|4Z$b_-wPJ&{?Y=t#_tq}7_hU!zd(uSjb6G8|F4xeryepon_Lv{XWJ|$U z;xF;leE6yczZrgj-CBdO`53>by>0@}+x`T`&HwWnv>2bv_mgXn3CRudxM&xGHll^- z6oLjd;CnkXUhwXfC7tW0OkAUx}e!7Eq=vkfbK5Zv(KX)tfa9(0pGrE^triGb;1NgFIv3R7-$oH6PP{ zXm9+Mg|6WMHh<_MYgq7AzBMw}<^}ZRKJvrn5o*&Hss@52ptY= zh*V(%CVXf)@xnyk4)81_{-J&81A;}o_;0FLu%!c=cnR2URwu=PT?7tLCb`W5i|oi3gdB-L*TwTzRix%HM7g$7s5oG5Q2H zipziObUWqlx7}7E50i=4;M?+1PaA6_;CA-emtn1o;y= zDOhJ*6gjE*O?#auesZT1D|+^~G&lTz19#hh2RHt`RJaA}c678|7XxppIlUkBVf;UV z|5w1@2Q6Q|PqA0-(by}+TM6Hq2YqIa?N5AYQsmNi@XzfyJ925~d67%EpBuTfqabo= zdtT(yw&{^edw$4T>GYmU^^NxO`T}#J-au%kc%&UV?WJ6Poc28D9baUQy$&4UoetKv z9-;gd$~y`Ypk1W5=?SqMb~b{i=#WX&9u6T zweHToh^Bnz3+9e_$ZqE02^C-Z$g<<;fv*A^JP=nKG4iR>_JnG5#0a@jx8N_C$2u$i zx#jsh5C5)aT{ZXamwfB;H#FS47@vA2`(_GRgKTwq>g%o#uhagRuOqW6vw4oqm-_sd z=)slbGA-onk<{no_1w#Id;RuQJ)g$@t7I>ZZ<^j`HJAD=)sNj4ypXx|`daSu*Tu%- zhcdYr#m1VhQ!?Jkk&wKHE{`ZTkI$SR{rqFvLmXMEbt&O6nTKigou2cfSMiQ$t2KvH z{xRX+g~|(_`PJgr(HdSj-E?7(`s?m7<;~2=EAA(+AlIk)?P%tF&j&fX=zB)FT9-Y< z{dMi8d>ZQ|q5FyVLQwycKx&^-apoix+E+=t@E z&bhJ*{3s7f#5*DCKgPZ0<9_B( z&BgiuWx(mQiwa&cU4>tAm!F!CPvyKz#t2A%mR;PzUUbC@Z2zk15}ztJ6}bleZ-c{O zcSig%#+SLfU2DCG_MfQrl*euhbj4ik z9VF*lEM58T&WZ0{=_wCWw+r~-4eBS}gf-^ZRL^E&D2Qtc;6IfQb3N~|-ZVk)1UKA% zuN}X|Hv@Y2Yi3w61or#H?pXBU%e=4t_2C~SE`mLBCa3q-wI|jRpR* zI2xW;du+OO)y^mcG9ZD?^To{$FWwgBSqtV{SY`xKeA@tFx_V16D zM?Mzq;y&qv5#J?!FT({6o+NvR8H?}W7kbz2iDTF9?&Vj3ES1mTS$xq2&`|#L_wG4q z$AG!<822iVwZN1BQ%lG}+dv-FQ zJ(u6_kNw(vueJAD>v`6*p7pF}Jys*ZB*Y9bA_zh=S|m`ln(dW=C_sXtpGI4dt~{ zr}tfoZ4y0%o0Zx1FG#Ea=q7sn;ZV0b}1I*xPRN6wCaHT@opoh_aT{vX*<5{RDa z@xz0PvqE3UHcfGoB!_G7K)4D@9%sMcN#=+e{+YskSF06|5TjXp`UB8&A2QD1J}dq= z>Hju0pG%__Vt6&h4J=5)`iSlnr9SOiM_|R z2F6Xrq+`{+c3Imo`2OXrrK%WL8?iY|mEzn z_+4P7ni(4#Kf`>;I0@oce)BZO5OU98dDfa6BW6uAV}*I~xbfoI9GqB3T}B=Fj>saDVMV)o* z@v`nEA1ZSq#*(*3DE>PP-|FLHe|m5^*IUf_;=%X~D|VqHucC82_X^~lX4c!1xm-Vn zBK#PNlb?FXjZfpr$!Bim9h;}jaV#FD7-3pBHPdDX_q4m2cHaS=Pt3Q44&TkA51si_ zd>2_wJV)_tzt6X-U-NG^--yl=$t?nP9iSF5#$~9gXE<0jo~5UKvxFt3*#4PCGnGK zXh%Q&OZ;fhxm0$ZcIExh^?Tqd+FBjgerkfXcx*EA1as%T#Q$VY&C~e5llb>X7#DTV z-Ey#es{Qm@^#60goz|z%GPZw3nXzJI41BH1NY_}3vHr!kYH$|s>YErk>nJjy#onYf z)toCnQHop>^?_?_inLc7WIvcVb6h1o+2A5N8Jq)C2W?WEiusfk<+*IdIW>}lVzjXg z*p>D*);Gk}=!q5^C+0&3?U~K@&D@a>h$CB)tX)xEqxXPxa_BQUXk&rC;cl=$tzA)5 zqh}`{9r`WA(kNSTUQILg#O@8FPcLhW9PHh!`}zBNH@BQ@Qdh8^Gsy0-d(Dvcpudl9 zsn$26P3x8ojp5WL{*%v!0p8 zH12}W3ul_Y1ZO*NwgUt9EU9f0d;NgA;dOYDioVM3=+fn1RV?(+f{~0W5yV~4`uH+8B*Pb8n=c|78x7wh- z?^GMXx88^UI<`ysvc25%L2qNgXXhRs`|(Qo>}n5qCuRC4#}0IWQ|Uam(nitM@LRy1 zPJ5j`o(J6s=P}0beT^Ek`3vsdYiC)-Jc^#@Fek$+)3Ntl z{j=a#oxxa5JRf-I{}1UnyO`5}ZBV|>6~2qA>#x1L@t4QbR%17) zH0Ff%uaeK)T3^WX%h+k@S?7Gb;>I&}<_=^mV~Q7)FJpa@?dnULrK1a<9#8y4a2!9c zOK%{4tT`Pyxl(ixKU9rkhzyDeLeV*S@AQV@7ulNZv7HyL5QaW$N>FDsg-~T}Sq}lFs zgPy0l&q?(Cf8~3}E*L&m1pTWF=>*{GYi@dXY~gPmT5#hYh|g+1c4Z^UyTYXnE`60T z))CEI?eKt)F-~osgZDw3{@8*3^Xi7i9(8?DV$#oS7OfU1?*oRC*7M@+;CFB{_&u*< zs9*9-5&H$`z_q5D%jD8rj?ZMd3H|yznR6U#O61G^lpC{ITbu@8MK)_@&HEhRHJ56Q z$eI#+(5Lkc>wWMz*h2coP809!EPIr`NJZG&smI2v1-a%B|0U=jwHIFE=u#r^u>SL` zA=WE5tl~e(d@a-=zn&KOw#u;H_3YkDk}L5olU^Z{4|u-K9nOEn$$H?$H$;4Fz1kQY zIq$eRP~VGB+`7}5C*5z%dGWWMzHsFa`6Z0^1^pu51o5sEw9Opm$g5HMpf24f;+;oW z%LUiFeNui#-Oy(6@-F|3&?mtSf~ho#FVtn? zWzfVd%6(dzixntG<)G77Twfo=aa!-zgsXJZA=L zef1nZ3cc&jzs6Z}FkdF%AJFpPocVP_oC(!d$@_9}zFcSS@I2!4H>m%N@#jR=t=-TI zF7RFN5stdCH&wp!h~wbkXi+q6hg%@IF|7-PKGe40@TVO+a>cayIlKcNmg>KC?S`0r zYQAh?Rc;w$xf}kaa>OU7XRsfb*tgEHrpun$s+24$y^Qzl$z`HX$zl#{(5HBYd}ie* z{SN;HpgGA&3LbVsNF6bJ;offE5Eli=$nx{@+Y%E zG_W0=@K2|ibKk`O?os5a5c(kI%f=!u&9jC055390{!ZxmUFJW@MXPcOdJ236J@VmN zTg+KTzJk#a^tvPTv)0_5%(I{U_6M5+ig5?+GUoH%E;4z~+A}!|XKdN$(Qimzk{&@Y z&*btkPceU>FE*n-bax^2X9@blXTn4E#tbPRJ#Z+(*;bs9!hB%jt>PL_;ZZ$Ci#l-HgrqA3CSNOM!C`vl$nxI z(Ui)R459YUp={Nw^_$ijN>kqd<=q{h*wPHw9>MylxHS;|(^!gBMhiKPn$gn0A=i85hM|Dcp3x+=vub}K;ww0^)9)?DK@`x+T z%y@NdVSmnyT&La8>$UXJ&?{r-{*z$9&!#UYcHn{ZxA14DZprPcTWwJNYtrxqO|283 zP-S_r_mUR{_x3RhUmPpt?zv8^KP4}C>qm6o$@1;t)Ly=4A7FHte$yVXkNR%-=dn#U zCQWK1c~RyOWAYY6(`)aK-YYTiX)76E%$H-ce)#70pEMb3RQ4kBH{WBeuC}h{j2LWp zdW+1fk}s@R>W0_{7>!Y{m6#m=j~Qm;jjy@-Ft3ingx^{3d+)+Kct$6&7u~-V|MEj_ zUreOg@z0Z{_aOWAz3llUfT4p+GM0^9e|cKA5}vGO{vrEvzq2>qYR9p?i8pGKbcuQS@p?veg47 zRmUEY&I6nW&V_gEyBjC7N8iC3s+n=M8z0R1;408ZvKy@%?m|yh8?$e? zi2LK%oLy*4s5WM9Xf++B*yHEv{|Mi!+{5_dynt@t=Un1XhVuF5GV*`ZiVXncJtRMv zt>nk}ZSEF z@u1{0t3Gko`6D*`I;(ONorUI|hbt`BZrSjnBJ_;0M)|UwhYmr$N=mCEt&{63q@j~= z%5d~IVP8B(ntX#i7&MM6zV(6R`#}qTj3Dv$t!v#rbJAj$glfa+yP!FT-#g#T9{(o5 zn6vZ^_B&6AF3HzEo!yQ7j5&4uyAhS2{_f87cR@3KJYVJ5{OembH6B5SWz&WLd^8Fl z4Zuh7=MAZTr#|eq4ugBubp$wr=#SK9@dP{iHnv1pSrs&tvw;($ilf5ANb;dbsS8rU$#{28! zaU4#ho%9iDv&PfxFn&$g78yTlVRR)nbnW8|Qk_5A#ChDT$KoxV-FGo(`#sNC^TY2G zoYSZAgwAu+Tpsv(g8k7I$YPrNWYcW$pQvCgEaDqvkkO;QipmOT{9@+n3Vaq8u}?al zj{kz<$E%l{1Hx4jn9F4joU2CN4(DQNbE<5&Np+ z>4lUpJuBgo?b%vjKzxCq_f{To<7?>)3_o)`kajP0Oi9kD&~;RKJ57Sb0{A^ zT``|@&Vw9JpHKPFQN@|0vp+kYekSEB{VdWs2XZ|9EXs$rDi*?fI0F(JG#76RDIaOw!m8;tA`GT=`5x1VXM@eP;N z96{!^pDC-cjrDXdb-W0@YApHqum1h9K|}wRV{fSM%2!Yuebr9Cs(^@>J zczaMS*tGf=$=4~I>V-eZro{kDE9rf*J0iW7GWyVyd3sCxBHG574zrItOpIFS?lONE zKE2Mr6uP?s`pWCPcJ|UIkqdR6WESIPI2K;~K)vHLmDg*s+C~y)@d&i|0J8pJ-S_Dp z`}6}o;wb3O`$!_ZcsSO(cs_7)27M)XZ43JSJptANb6pzEn+HwO5Aa(PnhQN%j2tsp z^vGWRJm}BKgC6HXdspMrJ6E*Fe*QdY&B=rI=8DcZ=W4F#j6MB%(3O)1oy~=&c5oj1 zT+tN!`tzV6Cl8vM3;jHaKiFK+4}1Ibpcf|(`k4!@=p6RBq804j=0O`y9<(wSx|pIo z=mPr9oA;@79`rHyQ}_;X4&>Z0d2`64Z=CFZ_^`X1 zdntKmlD90KcPV+xfR8iT=Uz_US>$~+op(8TUj;tw?&e0xTS(sWbY7Ia<-mvU*PIm& zUFXfcR(W>Y+!ZD?=UU*yCNFS3@f>gDy558WH=2-qzo4_m`?$CFe6Yz!``ejQvZ1MV zc(o0!4l^d&;m1+vza4wQ1bL#*F67A8upeEV-BSyFw?MZ6`dP671DsW4PRa%gwp-;XkLrvvgMX#qg5nd^!&;JK0Uz`LsPk zemm{HEm2l;{`#hx+aBVc;LdnH{~_*-=ktfTk8o$4-}Xvbjp+SF-~1$JGxxM&=jrfq z^c(el-_x9H-eb#Vk^X+1HIUb?fNTe77qaxSmQy(QoUt#S-wqEy{ZzB;YWQ;}{N!}Y zetQ17@7hjXtdkG@=uuRZ4+k8&sYjX-`7vEKEmc7j02>u}Kabc5TJ-hfv_3Vpk-mvV^M9?h$QI{1@a6YWk`2Yz{2&E9V`U9T@iscEQNK85rw&7k9D#6pVATrt}ETh{@_TjSl(w`h9{V-xk> z13q|D6XOe=;j%v&>*>jUlbXl)1lEuIUt?E&E!x!{(^ha3p|9_B>dG3ui1Bi#KODM< z@p5N2acZHzix@LcKtHzZjMH_udUazbR)?-N!n+L*efED?ru66$+Thl2k8Yo1ocaat zc8yiR%=%RI?)z_iE4?CiWASoe`fU1nF1RS-nKN19e*$jB057AhgQWcl__W8O`wPz9 zPCl-0??#Ubj*sB?EglQc8fu3>Av-Q>hYy75XYontk)y~yvUL{kjE;>d{)K$n;(=__ zQx0B(v`2G%#Yj_41$?O-+)5tt{^wiwf9N-gpWgWYQ%%nt{SS}R|8wDEW0rc zb<_6phxnKO=O(Bd7%Coe%kk zO*nGkLpGX28`YkAU_h6)(4WSI*LDvdvfX439?W8WcLi&uEY@;c%~{nQKQyV%SXQX3 zJIh%&vff%B9LbB{^&?l`V}6J{L45{(?a&wNDTBXIIxT-Zjd%-A8f#kJb&apr*bga8 zgj3y>;nefKImzAdw0+Qp^h2?EI!o7&9<^YUxoFvj%H!=5W95!6x98EjKG-CGQKx-@ zf_N=9vt{VMTllB;J>vRsKT2E#?6#b>t>8~E7We(Fmp=5hXGSsNd_S^a0DaMj%^n6mDP7=gq`0?K zxZ90>XW$MyHCQn{UQa)Idh!Hk60MuwTly~e9H{We57XZ%Y`@hm+4BuxKl%D3lYcI> zqVrcATOj_oqp<^Lk@jc+J(RDkv8z42!8S8F*Ta`I>}{=@)PB$$Yd=_vKXl?N>@o2C zi88Y^@kRE??cU7;t4O=qq>dt+g|piae&07UH?fpFzDpqI#&X*a27RV6(ZJqFs{P8q;F4L$bPu?(0m0seas?pwZMY+h%uA2mBU*iJpz)8nIF z`Qx?Pt175`554=FzMM*Q#+BbCR_f8I#BC>5{>O-`h0S|PwEQC9FK>&k7LJ3^u?;=P zpmp{+8ldw6(fGwrEU%gKhvloyx+$|_(6(8ZT@c7Mq2-gqYyMl`PBx*$7sDylbK>_A z$__eZ+p_{***?RWg4u2OddI&k|9Q?KTzlcdp6~mn_52bU8o&L@huMRWed$r)WdEXa zupM}yw*a)DxncQRW1BF*UZ^zUfDzBMxY7?!z5K>w*i!=YA5JCC*&Uok4NVze*oj>i z{HVBGj?D!Aqx^F$c<4yY>yv zA7K3Zr>}2(?r%R{+3mkPw|nXzN>xXKI#rL?9-F#PVjY#hU*w3-U&+2jQqPP@OIZkzZ0cG~?QZ5f~~*XiFo!b4tPl))RK(10<$n?KYq*J1ZUU#WjMA2k#s zeuIT98>B6LtC&YtG}ow(FH@$TEI_58_(!R*BZK|XGXjORf<<-p*>8ttw0`}}UYg73No3s4*CuUYx zM@_)_C(79x(aO3ZU_4iC;2ePku`7??_h_zi+wyJB_=|opyvA6^oPWa~oLnV)3f0rR z*N*>h>Uj|ys4ne$8T;~TGj_65=X1O}-*x)qE&YBcR@$fNL;1lsJ9>0%&_-CtV!PU# z?;jdg`a9t@`ez6AN{8?Ke*}Lf>d}52|GlELV6-sa%KRc(-OuDzN&80dr1%AL)&#hcumLu5zE7H8Jv^1ONpr*1(Y#Bm!_%*?4xC(5J#x}T)h2sIbtiT~P0-$FS@*@T8IoQ7 z4Cv-^Y?G?F@|ef_;aMtopb$T=5_BQi;neMgl+X9pidBzdUw(D~Szl!puQ=yX?Mbcz z2fJ;%?QZ6BeUpWKRxS1Xka?PZT&8~{U-wg%%I>2M==Fg9{f*jA*($Gu@>Gt>dzyb^ zq67ACy#18SxnAGupAm3;Lb;7u>GpbU)E-C-TMv!9zo4fB<^#aq*x}fx6lu>mFKA}; z>_UEdhQ8Ie$S`fW$SphhS8eWqPCB6z&E+Sy=fU@p=?;k|>5o5#hq_kQhqBnOQ=g2y z{>7^K;I-qHbbsNm6ixTn$ZHo?y+--8Gvw4!>5CtOmYn}yt*(0AtGnNhFDWt`_b?um zhn?DjJ&cKH>bmM4zUerUuKNY*?ynE!^R2#%zVf*$$$1^GEpp25><_29wuMu_PPZ-m zx7w;#m4B;8OYnqAbwk}Pi$b?uxhQn|?bxr~O<$$IPyFTPD*4xRK!3h~))oF6DxT>o zZDsGofYVNBvJO2Q{=k)W(k(kW3e&b2Jc?#T!;SiX+5|2-;Tvje;LoR3olo9TpH+D= zcXuqY=M|U{zlkyEjU$aAjU{g!4d=?{(><5PnmWD8V%=st>nf}5hv*aD#8xOl`LXPa zs{5fA(f>)%eKv0OW%9+Kg|CsOZ1QZj6?tO75nT*(7y%!pGcqj=d0;5 zy>j%w=zc2WBmv!rp?meW=tA@+A2;>6#(;Yc`Tp(2(@mkq)?wCjC)9D^UFb+{R{0t$ zhrp@sdKa%67A?K~xoVZ)2Yo$EA3vJLdoWrQZw2T7!n>0X?e|0bKGsFAzc;q&kEEYC z1|*B9&w?W}9G+P*9lt7M=2rClzHAeUJakH)ZOoeTsq8aPHKB6tH&5s8=U!;oKg6C0 zUAnvGa?gkFM_=Z}nyCB7vSYHvI_-APm#H9$U(F_WpUky+8DFbm$Mf=c$%D@tY!tLk z%r@PPvUedqQ2q7q*{@hb7IZrw8nC&GCuRHXp~-JpLw-YyhWEH1vxdq_NXxc| z{Dq`V=RVaQ%3|*@P+~U9E;4hkPO_c!g6#7i_z&>pttUjYzeNV|;$n8Pcg%VVUm24M zu+Odd6*jhiHfzwmtb=Uk=g!hgtNPgQ{T6-GSB4yfZ~idrW9=2j(CI{p(@=)a<|Xul z%kQ}))N=bJoNInbDEgVmP(5DdQ3$9Fq>X-85roe>X6G=f1GAgyL^^xtKw6^wd#x^zqU|U6EPa+BUh?^!+BKA{NviDoA{~loCGq$ zFup`iK5>V4^7Ua1lCz6U$|47;JYaQIJ?BUnK#@x-fqkWtq zJi-{$JQ!yD2*$|RhdkZuj8Oe`_FN)T*pvMW{8Nep4>`;{Y z4f>uk^p*kkV)Wnb^poTa;c|OD^!(1rRsEbzEL=9BcZ*6#hTg4?wS)GW6ZD&WRQIs2 zGzI%b$=%zJu~&vJq?>VLO=r$G*=;^_n3ItKC$S%uH5GjxaVhr%tjd7^anp>ifmoFd zkx!um;HqDjz0_up%V!u{JX?${jWfm2t7A?TZj-c8^+%D%o%(72ZK4I$|Hjxt_84WS z%XpRUHoMKD{s`+Z#xLX4-(WGG4SH$K=_>DD!L0sxdvbMjLBxrddM7dY9!sYKe|{PD z5yN!Z7c$bKZ*jhez9(-Ad7tz0z-#QybRPIUoqe#2GV&5HjxF4hehxD zvM0Ge$laYkuvsSV_Vf5uJ>QM3sQ6Q$IDVM-6Md4U-;n)D82v#x^d4c2vy6UQIJK~+ z8DC1#`AP5_#RuwOKAWH|kM~TR`Q*({DldV}kIsGi6>;z-e;i}2FCXAURb)suY;Rx} zDp|x(XGgjo$s?~Ji|QM6N^QWtOg3uI+9hor;QXU_uNObxf~RIvEhXW zE#f?~8OU~?KBAX470o6dH{Xo5G;&fAY9DDV9Lh$aHSIwOD zTYF~D@HDejtYCO%KQ0mCOYc1xh5VW{aNrOA3yo9JqiEA=4pL7 z`(w>3u2?O4_ts)2kX~aFD>jVJ0W{;DW$~dkW*uwHJ*80-UL&2^pV`w8Pq~=A=St49 zQCzMOa9L|6w>?2x3_X~q^O}mj>|?twOilYHYi#rhPk-s!)jz!Y+L{>gHQUY(=eB+N zqJtP#hJMEWNEDlDl{bx7)M9bQ+(XKVQZ7+d`r#LUt z(B@IE&7`$`(?6@tvhv!#IeF>wZ=SsLOxhd`5kHQ0cU--^=8`YD`n@o1Hmp;_nqSSV zo>t-t%2us{@pAZ7$yf!+2hh=MMIW;bz0yBGQ`=Z0j+BN|kF(FZ75xA@qkA4ehp|iX zrPv$Jq%F@LFhKjfa|jkf1M2g__QF}i#EF!y=SueX6O{ERK9Y}d6`G=__5Lh+zzFba zUhTgHfB8o)Oodr{uW3iWW9){n(jVADsWi+v%=>96MSp8Eb36WhY}S z#8?~F`4m&Du2xHt7bxVQ4>bl^H4v_cAh!QZxnIsET_&pCb()?Te#Wj=fKPHZz|*Sj6M zLfM>ph`4aup{2g7u(O=WzC5uJ)ZeS82;-T__jp^u&e2eNb1UAEl1{~zxE z6wj;lI-yu5`j?1sm)d)Te1<=w??$XPM{*P_M zPV5@lg&O>;i67QK8UMn9+4_z+ zP0e=1`L4anN^!P8sz~X!?W753(hc!#ar8)5e0Apsj()^Tck%GE9sP{?0WQy;b0^P- zX|viL8A0C$K3ku-Fr~BRO|(RNx+(dxoJ-#(iqR_r<5xHEJWFt~UMps;H7R!B|NKxf zyRqqG4lP?}?l@51w)KH=I}d9b%4Xzu@=4?5#aD-aYvpITxC+Za|l| z%uHW26gJbV@kKm0Osw=WWNg{0wcL^un!?)OEf-m2cxJpGxQ}jgV}={nAyK}Oycl4e z+RA)icRhBA$V9S(DL0*^W!z(o&8F;GE2GqFz%ec|~BZ$#$*{PaZ!iT9gZ zUvhzapMBxMh3g7uDXsTZbA$Y~6$fktzP*untcB|e@0Z_46uO8}FEN5|_?YB+(S|X* zTwaTBDQ#__52Db8@F{qlapcI9W zfVWUq;N%iwZWC)8A4u+b!R+DE&S}zBSZy!PBF1DsypOpN{BpLmsT|J7SCPG#iuXF6 zYJRWdX~`)*V)H`B`CWV);Qw~goy0jfwudvG;-<+*xQm%h`cd7cotJCX;Li*t=tkvw_u1D_X9CwpgDOhj+3p52@ z;2BlkKTY6Y&v!s?|4dyQ!0B&`!l^^ZfjhCA(0Ee*M!Trzx4^{u&vKZw;T}l%=%w=m5p5 zJQ?69Ha%hSAmhpy37&0qR~9;{I@#)@a}q6$%L(c7hxtx^sV_tOwah(=CDr`F@iycR z4q~Uqe>cE5VC)~Gy-r%REdC5>7HQHqkC)9{7ho-1i{65@ai!++809{0+TQp7q3s*h_G#3SmT$(}n{a)C9K7Q< zU~M<4I)fj}wI1GGy`(1x2q)4#>pWJ?-ximn*YvNh>V!9Lm^{DwpgF6`(LbZFlWft) zzSQvR=qB0cI%iV2x_#1&l_TJF=cH)WD)!0rjmB0v*Dv|*9Dhyq2K=da+9lPm+m}}N zCAF?AOg_gr)m?hAi8*=S_NsT%vMzJ9@aDz=3&tPcGLi222SMmP(yPvC){fged$@@9)Tk9PUor0I~bhH;Bs}5l6zU2(|#7-mjA@M5$tb2a}e|faaLWY!&FYpj^O}>M>+b8eX z-JZ=gWykKX5Fh6p%F1EbGI6Q3sOS}2A18iM z5FXS={b6Ko5044)4#{+!oA7JOb@2Gb2|V_(_Ve)g{#y5Zu&v-qF@Qb~zmR{DY;fLT zpI`uaOYwlVf-9pKsNg!?_YXO^3e1V`q7L-<#kT{e=8520v*P{O!z%xA+PAxX5;&Z^ z<97+SAGWj-=My}R;}#qa(nk-2*WW`&9v;6%xx$rnf-kfSA3xoJ-eTP6%vqNsA4-ld zgob*V^Jt@B$2V@?gkL1<3;8tk^g*+%Q|h_gF)vvHTPfL^4WAbGa47xxg&=T<+y7K za5#y1Xm0#>z<4!zu6$FJT*x=y<6ZOVWXb`TmH#lFUyv02$fgIM^h%xEGtr-}p8W3g zzsI<1{5NuEz8YGbQHJ)4-lkl|<9L7VgU-0s+GrPkBceIYd)njf_x3rygB;FymEYG< z=+CwiRsQYa6ncR{8#~Gfx}KP+J=DV9g7WvCy1KgmlvwrNnbWKLO9IvQ^pfh%$#+)o z_1#yEEoQY3dokx6yYGbYrw*r(fk&k#%CG<66IJ@R zKm9M`W;FOU`ab=w7bEfgwFkEAzaPIgR@@!I$AJ7^*~qfbkwtpq4D4-?pF7xp>$(74 zIdf&8ku$8Sob%=`+sC@nV6)nKQLtygV3UtNUH&>{#)pokywbu3*2ex-`=u84tQInt z+EXeS2@uB$n@eySDTIxK^K6VFnYJML1v{`oL0<&;0GS?n_Y=UVY z8`_>%j*rchE97(F#=VT0@Zzn=q^;z&v%W|K%;K$GcDx_|nyvMAJfZtO-DBLXc%R;V z>2zYb_A8y3qJ6q|`O@E!9|$jwncl@d_86W;2VyQN?-^k{i6+sNIdBJooqoKCs%x7YFs$jKP|gD z!M@1p(DXcPBNMsS&=0V!+xD&*LJ=0KmE}F`C;y-bEb!6rR)IsWZe^E zeX)fyI+%-lS#$5mVSMl`TsvvhdphgTFljHLGfa$*4JO|W59QJ)+j8rMu&eKR*_YEm z49$in&ZBr9nEw(K-ltX`#`ew7wt+eOOZ$oQ!I@1t(kDr85j2w;qTIWY^~1w4blKTX zOh$u!)d)88W7zL^fKv}2_KV-Cx&?fAv?}`i?rUS4y!<0ClvV8_KZXo$nGXCV6DzL- zZ{|}a`OAr`&Ro*g@%-4bUSR&YX!tajh7)sKxTB{K+XPzcyo5cd3rwh!b!UXObS}Y0 zAAG&UdNj;FT5w(Be%0AA34Mmf^U>3)q-V=)R}S<2c)Jc4JNYf>uf*H+UBs+85J6WV z7*5Ifwq@jwD(PO5>_c4*-nO&<6#c^D&^(jVaE8fl*fvJLo@@=NuYcgPhPD@2LtT9D zjfcbq_>|$BqOqVckvSgbG9E5oY?j@<$i(mJ#%6!NiQnP3;$4fZcxSg2ABA4K_gnF) z)O$WY4*v+9oW{M^vf7R?7V5a7yiey^&hzbDzLKmZua-<-VlK~G^2+7Ymz-bXUvgT> zq$P_k_b-`u`J^Q+C3#DlFV9=Dvt;rTAKyK2`Q#<{kZ<_*cJePPDOhs;0PW=Y*@YYEnQOFCCiUox;dZ^<(?1xsG7 z$zSsP>ii}9R~IaqTw^bpSL0ife_q~_>9^!939j}nF{|w*oi$lYx>si{S&7d{n`OuM z)l6P;DbJVi{P^n0OUil1ha_H39q0!>oy~KQdX6&I|77g=mE2poN4Q70cX03KzK^}T zXH8a{Sv_f(1}Akm+fQkl<^X8U7W#Zsdt>uQm0Lw z4xgn?kI!zSyl+A4;-Me==1H^A7cBeBFUJnYES^6Afcmx{kWzHALKno+oQ}i_{&_zSzyUO zv4>!$#~OcOCimzcKXQUuXBD)Y$)f>mL$BmK<8s>aEN!d9?_9dYsmwX(S!(p3{*f$f z|Gc+KWBTa+$yH~8qtEVlaS{V3;%f#REdNpz97Mr^-@$=mRCzcMkK1+}4*VzJ!0+Hd z@C$bFIl*ty$B$E=V1LJh?-MS3I*U;7`N(@5_+*1Q8TbbHx05ubLkswMOd1Wd&mK@a z@s%+6aI^yRUS##ibk3eimzy~Aqs!e(IbOMX&Ma5*p;Iopy5q{_d;*o*#5^51G`4KO zn9z@bqlvjA@jLgO*i6M`#=arIdms7Yu@8{84IR!N`fo413jd9zwKeIO)#6k0Gv;l4 zV>K_gGM1X~1xVl<>&(^6$<7>&%-qL#+}edNneMzN@Qsb7ePi*9qu<#(A^uU|=VQx! z;HR9jwAZis7+S&i5#Q1MK;fH=jIwWBq5Q^X8LxCcdM) z$vcZYd}I9!(|Ko+w-Dc0<>4D!j&H0V-&m)<8uH{D`)}lh$(uvoqI6!EyhXtGujE}s z9=@^u#p%3@$Xg72KO^r_^6-uIFH7fLO5QTydyG7MW6SZ4_2V1s)OR^~@{Ro|c~SBf zlD9ma7bR~w@D1VnwIUn})?f#9t@5lk-wJFpt_8kF;RDx)L-vha*PD=aqY3TBmN59- z*unrbQJ$uWvNTPEp^2;UYm`5m=rRUPY|EgDVQ6BQ`Dhz^8^h4RHfY0n_hHY2AHS2P zyfj_NpYjOn4`k=It)hz`Vn+r&KnHoX(3oV@erV$AACL|$V8=C)4n6Ehk`5g}8x!f! z!b>|yhX#Dm!gx8*!Rbn;|Dl(ObZFq}?WEK9K4@V4d-{LJ1EkaM(9lFWegD!n(&=*_ zeLw!a`d#VtH*__TPM=@Bm2~>rN1u;>Pk-;&LOT5ntxcrU*Dno_P9OW|>+$dD=hKx= z|3ZHg>Gbi{{iM^ka8c5Yi#PgrM<08c^z8_K{R#S3vi%hG@9)N(dFzPQ2a<_dAM_g= zo=sdWtpygDnlk*0I@0Tb_ud;TzVaYvnm`khX)@&*$@hwXaJ+o)@(Hy)A^E;ua{DlH zyySK6`_eq3E6qC`d0+Wj2Z(prX9iK%k5$vrO=5T(R zZ2qR66&X?+F8!ttKZn?Xmw3L4Yt)xrc{2LYkCXNnt~pj#13W#yo4NFe|IUUM=9Fg6 zmyKYj6r@jxiP2DQ*$umyPlIM^ZiMS~{we2dBc*i@-%;9ZOyeTt|5dkI1-;C_NBp-n zyhdEGrc=xfZ*J->EvL+Xu}x!Z@L=g~Wb@D4Gji%EztPUiX{C;z*!h*O{=S#F%%U!< zVVRwG4QCos*QDH*>7Av$#31Zin7qHc=Arvn<=;^%n?A)QtebPNw7alt^VyVr8D&Wp zi`#jX_-JGE?7zR3ZvyP6>_)bi9okOtjb5R0#F+aZ;@P*bE~P!8<;)M0Sf@|sJ^ReM z)M3{0z#g9?8s?f3&|GN>d+7(Q`?9c8*@rJ0@=il5^Zd~jb86bL*O_biocD|AqXG7E zIGeSnn{q_&r$ZO&CzYrB-=Hl+eaop|^V8_rb*Y!ntxNsoV|A$&tO4)h!U;QmnD}4x zqtlLQv||hBWL?E|I~VZAyP?lNaXsf>)X+^^_QTU2xBLxv%r*sjAEQ4Wr!C!y+c)DY z*@n+i&x_Fc9Pq9>)joWUD^KT~R?hY9xr7TJ>UfjM8hr`5M|Dq4!`~DRj$TaKg;!ru zvz%)Z*K@|7yFXp`9L}Td1rN7Vzv>nq_B(j+_khp5GIRrK^OMvs96*bXpSf_NaiIOv zv3m2HSF}#yUGcNfwWNe6;V74N)l~L%Phy`~IJ%n)W0ZIweg>bQ+_@Qe+Lmr3Yfq;w z-Ly@3QT-odEGQI+t*QJO^H^(I?Rpo&-+tRen0(p^L1x zUm}T$j9mkwlCP{5uduW@_FB^%E!Zb z@yXL-jnm<`=-?}(?D5L(1iM$~Y_gj-42t zAFx8t-N} zgHBIy%ho2dul@*L)DOfXX7Aatf2jQPx19Z-O6|8sZ$Yoa8unf0lbd*NMklTBf}C;m z@4)iIbQ-jiALQJm=0k3rzQ8TS2SDaHpLNo1{tweH;yLwGXXm#MTQ_WnezYd3psyDp zgS7_sm)3?WoR~rp=C$R}8}a><%oWS<1sTm|jAo`c^S=)rlm+kYpWfLR$?jX31&<15 z&&p>W%bmN<d2!DYOvL;Kx*BXzoW>xtQSm9J9X)nUZE`}fVv!}B-gZJeI;G3KeUI)Juue;K+ z8e~fvq)hRO;4`;x-fgDB>nz8{IZ1iwLf~uAA~ zF5sHW_4taGnxHQDtIFH%-?JO8N1_y3&cJI@27Vr{av9^bo= zeIfLq_?pQ~`$8**90d+q7S$b7oX(|_!>RkQ5gTB?M0}wW+IpAsSi2ZYZzCUdFrK~- zt$h#NZl z#Kmj3XN@*a zlM!QF=NGBYe)i%@89(~J{~T?{<{*?`24SO{IRk?y+?Umi3 z_JY;^Iq;zh_>gSyy|uN?I@)60BwBud?L@3)_Dmjno4qJ+F45ef{Ry=<0ym=)@gc2ljjh zfuo)@YvUy$?f(pHGNEqZz}BlFYm0yA=hLwJg5I}wk_V?FG8H7rxbI;a=lNZI0323V31_Jd1sc)S=HzUm9TTdoDVW zLoGhe;4>+Lq*Wd&pHW%BzUeOd#-*znoSDJ;1zbKt|HPcWan8)C#D0aonK9ls7VZB= zx^F%`-Z#Zg-<;2TSaxuf3G|OVfJAAxH`FXCW7|j1mBSy*XBgD zqH`%G_PX|MpLgenZ3N^ep_od+OHJJCXW^>A;WZ|?3|rov1RjU&FdR?-cp>)Gvce6w=(T6chXPddD>$f zj}gjV^NrNG3!O&C?ZihxhpG9!6Wj$H{JZuMqWLYrt-Wl=k19k%2BkHW;iIP2igYJ&4~pG_ll* z%eU$s$6m^%i??~s9J6a_qemBy+~JK`mo9emJ^(G$K^q#=&A=#sEye#uPvFQagWR1x zQTCfPu0{Vrc%_GD*MHXC;}x!hoZWXScs}(bVD{h@oZ7>k!7|K6Aa0 z0H>lS&E;ArC|0)OaOhkT5|*hw>gQne6Y*WNDa^ZxorWH7`RZob*=|q!p#{ukWvgee z?7&ts#ylSVc1~!k;rtQ$n{vDK&K7ePbfol)N9gLDCBDvvS$ zy&;(ddKs@faCUslC$YQ0US*-a+sxSmpA7TPobl~TLTmZ{KCZQ$Iic0J=7j1wTlvP7 zIiYVfP7i6#-yX>e-Lk+QYDc$la}{St-;F&q{w?yut8X8;f2aaIRk@XFxhDs`S1EJz z%;YVfv4_^)o)cog`b7-R_ym*6BM|vSvvRFxNeLp&i=YV22+5 zoE;imYKQXZbIHR2Y$u}lLYA>_JHi?xs=ZIzaTm`c%+<0d4hUb&8H(qpapR2{3%|U~ zF*6ZI-i`AIeP34gDfU9qyN%;2d}h3Xeu}31!@~DC>h;I#V++T>GiSvgK>pZd zwVjsF<9Hbf+K^HIGpawMZrNMvTiLaoXxAM`*L9WmjhX+UZ*mwv<8=*xJf1~+$KTDz z<5uST$eHo?p(}4r)?ECE{A4vBGv?%%cbL9W{Cb->y|SqZGG3bK*FN}Zm^FtrebG=5 z{al}IDelR_-L~I}Puz)*gab!(L3|T1YL0T-!5lMucKjaF-llBHLL+H@Sx29a_kD&8 zBpZK29SP?C1hzPPpeNx%YjLgV(3vmmyYueG-<4;r>6nsSsd=w|(u~~Ou^YPy-x*>Z zTqZe=JJ*b!o9r1q$czWBu(9!&6b`N8Od(g6D@@*Iqbq)`siu1pXKd!M*8%;v+cQSx z`=@Wt<6ZvST8|Gyzp{PLglh-*-0=;IfZnK-$Lm!{3vn7^M$5*IhBQl1urdNmSFAEJLN2o2g(6%z}h7Wwc9WeT6m}VpfN*vW7lS8P)HOos0Z(?(6T2?HVJNj>eUXQ_isnpA%mOPQ~vR zzBjh%CGahui9LLyZ#y&wX;&TPMrh+w+8+TA1*V{(FWt_lW6M>V>}TA4E~Bmej62D& z@@+qbvh|+XcEuI?4``@KIHmk1+Hd@)E@)b%ch~;*xbrI=eakN#xm_{jgS6w1z4X!B zCRN6~^=;aYEDEnJP5%2`jLY9^9xF}G20rh3CeNPi<;`90Sj3-|@uK-Ef$Vh-a?c>L zm*gDjCMD+xM-LxFe+DcaPuvBa5MKZ~`5Eut+C(x>INNM=^9!8(X5`^pZu4Xwcb(G7 zd#1c&GhV3Y!1>@qbK`36n%^YT>3nkW@=QLi`OX{5-g!GmZ*pw`Gw0=pD=h5&T$?p# z+ybNcPy{$-%l9qX?!by%Cs;c&V0{F+?l$s++XX9UyuA3sl^rL*ngy(GKJz*Gg0*%W zR#(2e@B~;TvueGTlZN%v+)tG4_GG{(J3cS=a?*kEu^sZC_r^Z!ob{65)SuD?bO2)z z_{2lMMLD8%of-Mr`>5xr2?<`UZR#8u7W;nsHVE8WdtMBDbuWqs5P$xf2Unhd9K87X zPBt6e`~oLm@cPE#b^A8=1bEAVx9_Nl|C+MYx7Y$~I&uATe1E!Y)9ZkB%$JX^j=QE4 zPs0y&J&QPJO{cYweH}7;x__NMy~<8;p2*-9c<&&*cVJpLw2^gQe-8H+f9^*7rZ$$_ zxxLfu8!pBsq`y2I(tIObYbUYUI*GwLv@g%`y^|dB%Rjkidq>%ac%;=Let%&l^Ff|< z&7F%lDpR3*#sAd@}fVXeWAZJeT+P*Y0#| zLFMoIGtnG)7tMKdO&4v_JTxZ|4(&Q>IkL6Zq4JyUMF%->l}VvPIPiPuL391vcGu?n z@Ml>Cj-lU5VkRtnOE#nIMR(kNSEB{}i4N`D;#ofcFIs1};18-a>4wj9@Im|@<_ytT z74I8zSW}nAhAiSG2Z%2kMTga{n1#&!k8&2@WBAtowA93h_-8gUL$j^8PcxFcDZhD= zo@XV0rso_zmn4VuJWbD~$%plvujiS`ojj`@Y7^(UCRbJB<4gUjyMs2QJ2ivk#d-_4Q5TPF>%lo)_Tjs_Pue|2{b0c@;3CYZF`@ zC#(TIzwYqsr+`dL8N>s%+GyXTQqTK5-m!fTwbB<`(fe#e=M!O_ z5}Y0m(T;^S<*}F0c9b^vYi=>bw?;QwXUb|iD(`CS@8WsIgDZ=Ha|(S^!5Xv+dwI1( zZ9w1Q@-kB$FWuzYulE%bJ0){H%$~5Pb5L6?aX=jutlMvPv}|pSh zqls7OneiTNG4X(&@e7Qu#4eKOzKid6c&&By4_WsVBz>H<_IvtVvG)^cxcV~Y0&gCe z@4)fcj^2!UKz%74iq^4L0K-=LRDKSUqn|@|mR%@(%*A2&y!hGBgQGV=M)q*HhqaBv zM;yJ0%ZGiO_ff~Wb&RpbNI>>)cH5}i7taL4HSsdakxgeid###pY~nPbM{oNWG*7=j zBe@|M;~Y-DLzh|H&)g&5P02-v;giiz(f7>rb$4sL7bffJvwOjT_Ux9ECmq!y@>hoVQkFM<5$Y2EsCw#I85PCd;~iDG2icBrE&Gx4vige zUfz)Qb4kz3j-N}AKGD29LZ7r`WmgNg!fPL6-NUV7t!3iqKHA;EoGjRyFaF^;j@aM) zQ2!lf4eZSW-kEi3ug+`x?7jFt(pE$N7{;>ZNcJV#UZO23U${{Ds$1h`i_Z!zqz?IT zJWN`j!yk*1H`1Oyc-1g?zCq9Mt6^~b^$h%ufZxnACp9V!GgmXl3_M$Nbsc4TedOxx+;Qgi)2HJ{72gVYKH>~~mHz-d5A&UyrZc~bl9xL; zC`|Tn7w_DO&PBXS^OFT_9S^IkXHk6{DMw|Zr#c|p2gBW~r=GG#NgMBP&KCPK{XPDk z_)Po--d|Yh%82d6O3Pf=+zYOR7vXC^Z9=|q^t#U8M3bFLFiyIV^&aWU!>6k_*$B?G zK8+INim{P5c1@t>a{POQvmiJ-%KwAlUAXDE ziih$G%&c7E9W?yxs1H!zS3S^fVm|B?uNmgV*XjsKmoA)f@g1>tF2*Z zH$YrBx1F-J$?dgUl@nQg{^V?-Gyz*`1|Z+GtW=a1`GsW zv1!xWM^1`=kM|PZ&*Q!1=t=Q7?=yM-B<~^K?^2tcbxdLMPw>cbUNBPY@XVt(c)Cew zYS_g8FW(6FT5B81_&9ayymaAR@0Gmku6-8aeje|CppMLcf+zfQ$_f9>te-vfGhN^W84iip;C<#L9bkez@KW zZDgOK`Pc4Tz30Urtt>~bT~0r^{YI<+)>m(&-%q1$j(mqKIWFI2$dZ|RMjOH1`)eN> zmnSvOH#6q@Z#F}kdm_x6@JDBC3ns0#-|}TuYMgIjEWY8JoFoqU(6$Tlk!&}qGl_5g zvjH34Iy?F9(XmY_c&o`X3lkR-dj}pM-EUv5Nj*iq&oa-puzoWB*ycUAeshC(ZK7T> z(v0MnSR(}QcjhzaJoc|a^S@_4#xMWCF~+sxcgJp7d+brlDf7+J8GoCjw|w&0TFyKV z5ns98S3GKAt8%W!=IO#4`i^YLdowE^X|>}sM8maO8!O(x?BrJ7`)=0wD^8vZ&ihvJ zOdl)|{_n6uiJf)qXA~zBT{deQ?Z?kZ-luJKMvlFnE>@PIG7T-yPr2IBUCI>`{9#2_C^U2uvf4)c6TCS7erG03g~!3vs(&(IYT{jTo5~fQG;UOvc!XE>OOz*ksq7d2PTBfz zak^|h3tt!UuDCFa=U0S_;IJM48)HH5*x$xq=K1C8KX733>Q>*KH(qyf@~L#)>Z@NI zSNB9;slJb$Q1|J)f7cnm6F$|F+ofZ|*|}0{Khe9!?lI_6YZi3qj!fx{;V+uhOlVJY zooJ?r=Pq;(t@yJ@&SKyA)qcj`6UafDD-#dkiv}&o?|cY5HR&-<=bsp|v@81*DXw78 z9MaUc@=I@KO)NP?Ym`fAuQR5;d47d> z`{}h9>-{zAd#zg+(#|{4C-$KizJqdtcRjfBcK#LZ-c}S29fi)i`Csd`?YY<=o@|A- z6=0KCj2+@EEA%Y?PwZDg?@@pJg?m1{mK$I9XRO659k?g#K_u4-cC+4%5o_bqDPPWS zA=fNpy{yRA{iPQiB z{rOzMeiCtA1(*K$roJa`mf~Po>DX{V>4X=f`$z9FfR07}2<#m>XPcIh9-Ne2okjfL z24WDj)yx zmS41@wdU@tTWhEn`p(X^7e>(G=QR9@^wUX?C_Vd$(ukRiAHkFc>r?E}BM(Qie^F}J z*orx`P%(+0_xUSjhnRI^PDrw$WU($}#utyVUceVX>GhV`r9QG~zu=U8qeZ%W*H^I4 z)ptJ5t+b#)>G*=sU@bJLxMPZCWz%N)HI}cl@26ep)_qCpYJB~=4~m^UzH##K^NjGF zzLC$JcvKT{i9LNvrmk6goG(Lf>~|#VRLA0QNIbX+9hU+BP59?Ui4iI~##e9|x`u}~ zptCdK`SCA9M?TI_HeoCN70yvvgME2B=YyZlnR7)vA7d_ko3U)Q|MY(G^b%iDF8j~; zCC*BYv7h<2J*y`S?LT(i2dwo=+MWOhD!X$@ICRi1$kq5`eS6s|duGF`oI|A>Y%`>o zaoA8SU3Kfg{To;>en@V2_V!%+ohsmgZ$A{Itp&tp2+-CbZJoK|8*V#^p~adFRTRBfCm_UF6_l{FQ?KvZejkwA4iA?`H{fy zOG}VXs~LCt|Lq)enf{kA&seFsApq^G%uE;sp95zGjM*9^(9bHn|Nd3PgcF>P(~mWO z7oOD)JWpbiA$Xnx9>zuE+YUUd$KgQ-?7*XR`D^GK7oK_-9(-xW;b9+N@Zifl0S|2* zhliMX31XqDOb?z7w6Pw13)ijSJPN*B9elfVfiDW@!N|VUZKLq{Jn!FtRy>^Fmu};x zbQ|@}OW@pES9j2+O-<-V2TX5iKRy!9d0yCMdw4JQ@Sg9sQ)5bd&(Xh(EmOH(JF)pM z7JbS-BS3$%hRvOzvtftM668hsCgpsynyFUfXk!0NIuXe>&b)zK&)Osz@`qDZjGq~+ zF9{tw)#Mf93v}pKU%^ejlB(V1^gr|A$g}M~sr8vr#p10%wmysubJX_r5U2EjKalnh zvN@9$m;>w?lwk|qX-tFW*UvIm!q5P`BW@3m-BXJ_^^s?LU)zYB(+sTOr?4;(yJvR< zn4#_8O$k)*?qm-Fd!pY>nqR%!wr*fgdD$-iSv=b}g!$&C+?r~8vSe;|Z%s5IzVTD$ z)+l}}G4w3W_^~u=4q_hDci+WEAbN;7?m%RSxj!#@$i_C`YO7E`p&!>bDP*V2u_+zu zdw)*bPNw4v=^IbC>egRWslCFZ%neTwub~&&{Yhe-Djw&}%#VjT$Do<{Mf~Yz<_+0& zxc_M_TA2K2?;rdHyni}~8?S!^yZ+I?7WpUE_aOhOj(=6ZcTR~n_fKKXRRnLCnS742 zKS7K&wdXG4276_*{&LF($6ncc(s9n+^>JZxO}gAS->4nPY z#|AZrs=Up#yCEaGl&=9_^OW#+e?_cMxwp_{m#8_y)1pUrGPTU{9c>`H*>DO6P z5~G;*I(in*{tw>Fo(6g|lNJAY9GZgG4F5m&wGWD2`{~`zKB@0M#Sb=vQB#Xe_#SUR zjy;pC_#^!H{#y4tYk3{9$jo3MAk=3yJe{iOe*@TySMZNwwday`%ls zDO@>RxjWiFZc?$SvBvfV zW`^vA8P7Et&!5V84yT_Bts(o;jORre&&x8NFUoknJmY!s#IyM->wEgnqD=Ldw-0O0 z2@i=cmv6K#lPoWpMg4OZJg@fqj1zEega0~k+yRd3z;R}uME@90&A^7<;!L|bba={B zpFr!4>a%DBSsFVi?RV=Lnr=HCp5DSfH9V^lyQ)g!w>moOe%iN)|LdWRx@_>x|1B>N zpOZ16G9%0tVbX^2`B48Vt(@=ArAuY}zv9K(&pn){J|u46LN|8mKE~vU<^LPBF4$YKfmNGCi)xd`gEt3ydnJal z9KQ_4xg%F%7uPutSxfe4^n+|@pFrO3a@r|9esSfS$PulDth*^QMtV_6I7NlB|BK7! z4;X5+t{t{(4>>k{@Uro_B*WgY%fTD*0$Go?(5IdBX$x)bBwcznwYl#!CuSP$p4h7w zO{m?iw3GSm+-SO;JD`u{>AvWPe)`zQ@1oo;%I%`uR`OaYSMBZxui7(jP2=B#$Bk3z zop<2!CHqC%1B_~q$G<$j>Ci0p18P(4`v00nJzBvQz~Lj~{hg)__4O2DXOGjxiQlE) zy?C0#zKQW;Yy70!(nMRrX`UuLWa3w2XdJ)bAV^>TciQz6L%Td$ri=jiAP)CX)W$vy zepH5RH_Y7-cSU?C6PJQZypJ{QCfDD~JxfA#xKZ)cz@O+(Hn8|2Bz1O-aI1FKZnSO? zPyVmQKXMMSVu*EPh);lSqdA9h#(c5-|MB)V@KIIQ-v2%`Nr((!AOT`R%}f%MrifaD z1nX_iB#0Ii1)^2k=Q5LkwZ=CDEDD-Q2-e$ZZ=)zy{a+@$SWVUIRnbiQ+)DtpzV@P2 z>tkP>;JeY**0;o3^Z)+NnG=Q~3HJV<&woCj^Eq?&*=O&y*Is+Awbx#IZPHbL1Mf;H zubeSmmTR2;bjBk74N>g-dapI#WvTp2|LDfD)Bf%yrd%d`Q@Qx^r+B3JW7BPx!ykn+^|ku713cBHXug&*l_nh8f)^MCuW-}^j!Kyy zE(J$(z!B^46)og@jqx7?H|xNWU{HD9x5AZAz97)c%5UPMdc{{3Z}LrS%%zRujmq!w zMfEkjwvu>qtUDA`6qtg*N^s-g!r^IFMLIrSXN-FF)dGiy55X*7R9^}w>F~&(MYz;= z!e=Hf#XALu>eOCAuRe`kr91QK)Od;OY(MBYhOI*7J+g-99QSNCY&!|9TPppPX43zv z^T|Z-R^tuu-^gh9+_fI|rYg^OFLU=FsSfbGL1_hwU!UW`ia(7z_TNoC(_Zy;`k2-? zbw`>F8Y>!`Wt63~Qu2EITzl}8*0jQ{qjBo~dbgLD4Zr4nI=|2Vz3uo57TDp$m8`+a zo~t z=$jbu2U%N>;K!zZkITlu+yWbJqN&IvmQr5~najL&JUR`}oc&<3oPCb#_X!W)+WJK1 z4~5KuCw2aea&yqZ-av;>QodkQ{lUyLFw*8Bsy{d|FjMtwT?ZHz3Wgx~ivWlGh(!3Nca#WEF^QeX8AJS2(>=P~W^8zJKyr2+WvBOx_f9g) zH;(gm@x$=b7V$dqILRR4S}})3M-kc<`y=N&Lw^=^gw8r#u~z%{vEOttbl1GVPTV{L zJ_vt3gFcuPGwq!3x2P3*()acFV+nr^<81b%S%Fq$NzLV7S=_+2qJ@2Wl&>Y{z<*1)XTj0`BLiDTmf2P z?4y5cu1&sOS+NPF`WYD6gvQaVdA5RK0H4|UkWd3gAHyRpG5W?)cZJZ zH-*d0wmI@H4WF?zefl7Lrqz|+2i>0y-(RvZq~|q#!{<#n{L+=P4t=M_t6)|?Ed^I| zz*QDBw-j7RZa08`Xi+|+hs1xV%+0fsu_)Yc$SDYjJ~x!=95Ti~eUjM^q2QtWq2nvE zW55HTDd|2>5@#d&byp9%kpJq(`gv}h!}WKlC+`{bGkIn6X$Qkq>N_~!&eIRh2j4tUw#_>@&o1P1W}O|U9-Ob# zvAY~to^{H>`C6DOXO`Eq=-_<&#vYt6eAdDFYKADUYkzEh!(@dNUh2J6D zFhpB3%Zk<>y1pL!(0Q6?9XgLSlRS&y&G=i|-(n8KTIx<>JZI8W4KgFMKRVt$kUs}U z;1}~U#%eIdFNV?$y>StFi#7g?u{0!YA9HX!_xTT|?VgH*^BsiyX$R-=G5^kN`_8Ec z=c^%xNoGDzuSw?zyM`D8%|qxir9+h0gFcgqlcCBB7ayE2v#mP|-8}U-&6z!t91cxN z<-2v{e9if(e7BFBuP4vVw{lUK4;&xKJ;TeK7&7Njb%y?XV0(GSI7+5;n4x5Y{8ZCrUZj8c`sD+Y zo;S(ayu-=!2Qu<<{_(JRwa%AbZu~F9=e;f?Z}LyW=e;~5ul?TedB2m9H~!A>dFN*2 zHE#`{_xOywvDb&s`<0Bm$)4f!=4RxzN6>3N{0H@#^u92!3=hLo8RbTPJAB@UGx8>X zGko57Mqc}c;q$g-AUFUI$;9w^pUlW>|0DQ)c=&QR zWt1CyaQM7G%E%l0_u=!d%E+6%clf*!@@DesQgovP>E2!#&z9-gkY&#_&xYDsSO=VR z7V}ZtjGr{cJls7A8#-y;98)$~*Y@iAculhLpNAL3kZtLFt)FW2b*l$j z&otvVvnJ}pK2nc8Wixx64}cS!MrL~!GY?76vw6(HdBPcUl@LBMFH+Cmxu*RH>>b$i z3U>JNdz09THv+5HWjlzql4S1c!`89SXPCR0*~uKc-(bH==CHo~#lZF+d=X?v)_hEM z&1UlJxs)~$Oxm$SsN5)P+Ap)mxsbD(CsW_V`KJ9v)_qm4)_Ya2+J#S6dp-4zq<0bT zC1w8@y53o-dR4CK4MKV&@4{*mh3Ipe@O%~woi1iDt8_%h6l+!apP z$CH*LT;jJmX3AoEZoYjP^EgFEpL)jJ$LL{n=H^_xe|w==cw z5T&mgI$0m4%n{cNun#!84!#H8x|rAJ-OS(A=aJH(@%zv4;qV9XS zQn3lrzYQjb*YAkWc|hLyABNBSW=7uR2=l2u8F}rOhcEY+8F{;396qnk5IUf3dxy_^ z6M4OHT?bAEanG87Gu}k+LyWfz$m@L<%D|;$bQGC6l>OnVjJ)`&51V&sM&AFMZBECJ zN!ba8qMQAGq3*hW`gisSeXu2?58@-tX@8uNH#mY#=<1BT-6Plmh$VXf9UN+XBfV{r z5oqy$XO#Po;K4jUe1D_c9sqChx#9D&*TGre8;ZuB3DBj4pb-;pXqWvcAPj6C1tSu&ou zl9SWxAYPQyzLlx;QojFgDouOP((64ZRfguh>FH;s@;8vKxvY;ltNe51mvi=U+RMFv zZ?}A-wqrlme#R|6AA6AS-7dFRFZtZRe8sJs*bnG;&M%6$;Ope>o2by-Hhr$^#VP8} zkmvp(=C}Kp>t*I!hYnVYK9xQvtU-1uUp=v)iCf>e5?gy+-iD%d9cD?!H~X+LXO>-; zk;j*+Ka`grNG~g#q0dE$E4Pw8rkeY0xbD>c+k9DCKQGwiE9?y-_hY_TVgvhM6IVx8 z@5O&f^-30dI^yh%^1_32z)8;Bvl}0fo$y=5lPTppZw{#PL#a7nyu~>FUC_?~I8VoS zs3~{xNbLv@uDz$~#gAmDdKFX0ox2g|F>PMS{;8R}fYY59!L)K{g}JA+%PyjHgSO$H_mR7;o^IiJkJlm(SV}#AhJ}eV_xM zifa7y$KXRAM{XzaEp+zAAL06S%AZeq)`U+7Cdc7--urOjf$vA_{XExykUVux85wyj z@(8}oGwlAc%yaP*TUZ8c+}nf6tM-}#>lxdaHtktv{4T1A5g)~vUdDc1xBxieyQc$Z z7#~^j*MfBQL(>yGm$K$1eLvmrS8d}|o|$R`v7svExAfX)ItzvU zml<&W_LG6hf=h9M25~I^IQc83=Iy2QMJ;fbnW25;YKB-N+Bd{}FUq>pD*8ixB>3ga zE1s=>+D_cJ8e<;5pLsTMf0kRcUAk2Szr|JXOMSB??HloXc-g~$CWg>8piJ@WU%hfF$-;z1_lZjrSjfBD~9W^M<-7wp(Ki{2D)0`|IsiUMf9V z*lrc1{+oh!t580$t`En+y01PTMILyz;n2ly-1o^J$WPktS8No|UaWX31IVn7CE5L- z!v}7hW9n{e!VWpv-OFK$8Ka-GS9!Ln+i<4#{Kgi=W?6|%d~*hQZ)NWZa`=Upuq|$X z&DZ|YA6M^<`|{g-LE_hNX7vhUOmB*4Kk}sBF!ZRpj=@e4ul2Q8Qdax~_ImMMe3t2- z?1$%2p6095rfCm#VW(ug#y7Uh@!OGMO~fflBGVorw&AU&Z&nPLUS&`4*BJB7eq^xY zw?3t_QvO)j)|93;2f@E&^K;0nICcqrb20zZzwzoA`rFBk=sR^5cUbnZJUh}(dlLwp^1rg4sl{*ym8#msz& zOSpL-KBxC5(vBU(xH$rzXNXB3Cw@(wxF<#6gMALfVu>Zp?^o|-?6q}JMhARY>A&EA z@WMz}vZQZTldq_;5j$SBe`2G`OtPOjR%B*==gE`WB7W&eeY0A@kIvcI0KRSoUwT$7 zvKaU}pLjT{PBmRB=SPhFII&t3d&S6)9N0pfzc@e}JAiFEXPU)6v=ib(g*WS5p@POA zaUPZMB)Sp3n2@Oy9~Ygd58@~F&Dumv>r1dt#__cd&gOZvYpaO+Y7+5TJdfi&{T=t! zCW0sO3?F)*HpP8&67fo&C-L5SJ|z)6foJxGD>h!-H#ZSKo@e$|OQywrrzV2O@m$J# zqs{C+S{r`Y-`KL*`*(4WB8_>a_4afmv` zu&-NoanWQq{`I2UX5ogqwiAP%4SbavFQp@lG12gI?2*kMMgN`vt-QRVE0||ycJN=k zV}Sk+zI;Mg{7>WwK&!kr#Bv{ePyajbfhGTN_kA*u_8xk0-oM9t;(zJ;?!)%K_vlY~ zM317wd4CwFlP%{^usiL3ZJ<&xCSP-T!$@r{pv)Tj-RtAc)uvx#W~i~G_(U2{!i$j&;#78?sU0PRp2$?YUb#x4PgMB!0Vd;k(>4ylszI!as(8gFjkLLXto}q;s z_whWA_rz!xZy2j*bXS`*SH&A{?BSXDF>4K#;tgZLngi1(Kq z;(hcG@6SHO`)?iM{XZSz{qcu*Kb800|4Vxp|I3L3V6m3ydzpDkN>2^3e?t4Kq$>^X zcg=Nmr}TX}gWvbLbDVt2kiWs;erm=Kd-$FB9EP||+OupS3uEldi=d}S4n#^>2b8MaNo#&UqTK0f|LH%xv^z_G&2x#ChLle<5dqM%iB(IdOmbV$yXM=LOisB9~f; zSCRRbP}UDhv)i+QNpr17@J~)^evxY@a@k+b+4hMj<&Fxn27B1VqP%uWRvL0<(%iZzjz5{*5s`#^36flx-+Y|n&(hnYtasLN9#0`cn?0a1KQsL zuepT1qme5(!<0PHr-N;kE3JeDJ}*Jvs((7vwgcW-N1Bfq_Rpbj)jwU@wgdj@q;Zz| zFD0ng5UU2rNH{wZ_MSCQS%QT8(0bw7P=SmVwHr+)C5B^-j&ditg% zcX!cJ`oIE@7C5xPVK%t6z+25v((nk*qLklsrIm;r>8@EG15DA2I73G9KGs@y>>Swi z0{cjNd)U`O{EwnYQJQaqvseBx$4@>=zlyI*4tRSoL<^b=`bd)Fdk4I`+l{T^8^b$jzLxLmz3=Wd{pfvl zzPwoCfb<7Uzu_#t(B(Pp;sb{B+!@3F7tprRzkGQ4w^T=qD~bA@Yi! zdoV|7g9mdo1#=6qETO$BHwyfr?oCAw{@8CnsJrk^X~Kb#ZbTbo6FVza_g$)+HQu=5 zKimZj@Ojyu4EXcv4yED7+n=6}FO{i%OwiUM=BbHC`5*n$f$fO?^}y%)eFww1Q{TOt z%7;u-`*miq+JY~$gDcj#HvX8j7=0iZnvpLCei=jlTu1ufiB09p*>B6ydW2|1wj9xj z=VwQ3guy+-vZF>`G5zRmA7Bz|k0LMPzSQ%c^7eR+`+o<0Z1zlTkB>Is8$2Ozx+l3Y zANc43vRx+la%QtnC zN!K}~=c1pj8;!mSeu*bLo_(1MH!-H~2A0-wcH%moU*-Ax;{L-BP*OW2oQ6K^ewrs^N?WjD5AixEtM<-WsRSdO3_RlwxKUZk=% z<{&qbw?Cy%RaUI!_AbfvdhjPW?X~8P=-FmNY*%qtY}f5w_25}@Uf<}QBmec@H>FG8 z>bvMk%?Z6*tn?Pr2fuUE&mjFa-&aVV(iNc{vX5{^Or2M*%9I{aOTI`AGVfaUKf>>Y zy9t~@pf$n>V@6{jGTKU<$XL_=9q34LXmkf{QM?0ZFa7G0#qkQ4N67BpUnV>1{~^r81AijFRa)`_K-Q28o5h%Hs+r4E-yxzz-%7Cr-9hI|EMf zCgGrjemdqI=20iP|L^7hDZCRcxbxfm&Z&YCdZ?ifm0o(>!Sv|(V-VX|^Ahc+4};75 z`0w#>i?QzMvgn%+mzIm3V~ZTV1yApWH^ta%<>;8sc`UAusdL^9>lc~nLzNqaH;1OV zJf(^8+yu@J)QxvIu`F8h_VW(U{#z0=iT$w%+d{q z8032nV`wYu3pyX?aqzdEarihm%qD#^IPdtY72cn_vX;&tgsLGn|BbSILtT~ z-rPBXt9vbjKHhZfBAhdwYJUgqzl*l(-J#m9wP}rut>A47cv38zn~)1z!B6b#N}~*L zr1`P8o+I3!KrBYZ3Xm*VQIpe7j8Ee0(w_qGGRI_idsT6&5 zhL!kMinrG?ejT0z-Fb4yt5>iF!J+BS=1ke>YR1z3GiBl9!i#ieVk0K%7ceK%x?HYF z)FaERF(y$SVDESyaid6w$0te(xX_cbBj`#Oa$U^xr(Js06ew)x2Sg+cfsKYHa~EAaQM^U zuzGjVX&QaHQ zwe(LGWxSs1r}Q$iB9wa}S4Q9XyGeVJE2D4H>x|M)$r|B10<6n`bq@c7)Kv|f&|jjQ z`pbd4x{y0{1*t1YT|w|41ph(sKb5+I)D@(zAovf0{~-83psqAoR5pENS=6w=cIIHu zemGF~EoUt47@WiTPDYnzTpY%Durvl359#{4$N!yi@B!oCwEC)g#({mkd$zLPE7oI_ zcDjDx?sGHG`A%K#a|h3dr#)Ld2hyHHJdbjoQ?{Oz{HXTi2k`lTPx@aoazpx`;FVq$ zH0b%@K92v1l^V~{dEkUHql~@RrR&$Y?<*P4Ey(0`HRIZ&#KBowVBgbV+q3<)nVnT& z?omE7$F$#i-WMCA{EsX`_8{ME*0J?XO+oC1C^2o97Sy6!hxdO+K7Dt+zT)wfsaTmDDRalehT#;$KwXIVGj67SOcRu=4P-E@1_Y|gN}DteCZD$gD!pU8$B zmzP_rdv)vuPq)B^*01vd#CzY-8KpI*|EHwJbA~3_;+Z3?A|~1Poc#IgzEE5y_Rjcf5LnXhLJDau&z?V_tHBg!efhf;W)dQ~fO)z=ZS)7AHed)MTnS$#zV7i2|tRY~kragXO@&Qi?@Hnz4 z!{@{M&Yx-vdXdwX629}NzI%^$yas)Gea%{H-Kr~(T&y##6n|Q9_Yl9`2Rx#IW3Y{u z0+0GEnCdh1OQ+AODOY_K>~`Ua&}WAJvgoUzV{0>Q^2a(G+FZn4<<)TY(9RVp+Sv<*Q%{L}-DR-0hvxfLDyNmp1q;Kc< zx$6qjx3#ZH`nG3xS%U6tvB&8XY-W)oP5&zFMIp|AgDTqhSbnTMoWpfK z*VnmD_(Upu^}-`78|-Jg|` zQ`e-)1LiTZgSllDIA#4O&#}AMb8{DK(KRM96We4Q`$9Fgzx#+g!GrH zzEA9YC;fiX(E*Z5XYP1RD!utZ($UM}#3h=E&z*4fr1*XcS25yFdARz2z>&_dJUf+d z8g?5mUnDKx&E!*Gy^@yq?KJ!c)AC-Q%KKtk-n-NCCfWbt)qhPYukhIHlN>GRoH^Yj zw3kxPp6(<2-$rQSN@!x=b#|Zfy*k%)sT}rhj<1C-8lVf#*{jfexuMFmH;nVQ-G7nU zp#GP9n-9N=QI5^NqFUOkIOQrQ^P3L7QCrmqH{ug8Jbfhe+?IL)E*^ z`7d@H?o9WpxvPLxk$!P7+~Lp$b~{#hwJ*QEA5 z3fH9>xE79t`;qdre2F}*Um}lUG8_u#v%f^1bG}5LbH7BM^U34+=vj5HeMh{`;c@uo z$xp9?^;6k{M=MAx9pzTb|MUP^@(<3^2@8b&cUxuJpE-~ zVZaK$RHQx4A?Bk&f8qRDX7cD&Uq}BcL5C>qE}Cd2R@@jgb)OJ3G#fkYz!(1I)L-{1 zYlCC8-*bZ9|L8xTwfCh`vvB)A7cRDdjlEy}Fsj*+N$B3#jv|vxzl{%i^hk|StMep$ zX~T!R^O7VsJTG12wUo=-3mq2UraqhV7gUDImHkL{di9iK)`QQvSKch@=^1zEdU~K& zwI>Pvd-a?SKP(KG39I1ot50NIm%jLDEIw4{n8cOv>&W?bVkSPM=A(hiLU8v3aLaz} z_RpAmf!8+S^hKXN`#bnDeMH}~ZZIJVUeESVo-aR@iNP0(h$UJfo6tAGYtw*>SH1J_ z`b+Ri?7{=^TAbP5G`yZndz-%mUgZO#`eOe>yncgvqFPNrhOXIFDh&IaLH#T#Jszbb&Pf+shflBq4_*V2B zJE1;W_2$6L6QTW$zR7`?N`J$7G_gh9=!-u31-*RqO8RI3m`!k35iyl3-iO9N_D^5j$t6sPvjrJ#v&_}buU1$W{Ri)Y&0(Yt}n1;K5g!WIQo}DAm zzE{sqXums+?w#F&iGuj=btjx^&dO~HKiVGl8_D}%o&+JG;l`f@t;&R{GG z?|Nn(uFmUQr_bSglc@hn_>lVj|3*ItQ~eyGpQDqoIZ$>~e#t35n#tcHtSt#9!6%qR zcNeoBrTo%I#Um|z_@$Rx8FU!uTYcx1{pl{^OJw9>Kg=Nitb~S|Q#54gA7?y*L-BBr zS9{}8GC!6^N3w@|eE7@KVmD>%qzn%a9cg9!L$FXrjIvbD;|@J`Idc&C#(xGq8rr4# zh-h;0-|T+H;P$~=YJbk01bHnv9WG|A8$EnW6Z0!@>c|DH?OtRjZ2X?>72(7Ytf`bh zuQx&$cHoG%o~y6w^Q~-7giQBU@ejkZ8%F!uY7Y0cg{E~EMQl6q-K~;ErZH;cmyQ4U z-?Yxl+TcMtzznneUhEZ7#z+kRbjklHdb%tBSEl5DWnbOlW@Z>Tg7~P;M)u$b(fBO1 zV6#5ZKtIf17lzNJpV;?|kd`2_X-ifboz3MS>bfM?+#0&R(=)TVnmZz;Y4TO8+$ z#leyOuVySq7{djBA$}o!AUtc&nfk^!JPSO7Th&zxKQpFywD7u#vWt;jYry|8#4Y$7 z`6|85ktN`F6MEelDg0gue(x+Z3)g^O=%Zb9J`_(Q{yDtR*Lf~&_JiL@3ctdI+g9*F zyVSNQZ52NJDZXY>_!T}q{CfOnDEy}Lj9u^^J5`rpaO?7Q)=*C^SD3m4%Pz*c+SQ%n zKWfJq^bF!~PgnyUrC*!~9|!}}Zt$1n4CZuQ;wat~gG>1R{IBp|?dtf3>9-u+`mi2*z%9y#8((jyw=HhrfuRw;#cBDA@L#3K6YdSG*izwj$XtZbeySt4PUw@c+e&IHeCsg zNG?mxi67Skr+AT%^XIp6p78I`yVgP@P2j3@+G9mM$i*6CCWOqS`QhNsqLa;(3dz9Z zp_e7-hmsYNfoq_VJE0MM_dYb__22E#Ptg$L(W961XjfT^UZm#_jvd;!lxsEp7o-0q z165xsv{Fy|4kZI^_M-_V!6%pmn`EHcCw#0yZ(mElf8sB$SVP}yj*lMQuJc9ae>zat z17B?5+J`*{eykW4{hK}Bh)$w$H8OAfmCGBQapBHqYG|iuRqgcJ@6oCJVnnAl?F^^M z`OZNa@o`q786SdnqR>uqFg?HXKH2hVr|ZM?=smJC%bq5>_zpCq{Z^7^bK_6wQ|4g*?68n)~dU{q{w=b6HU=PFztCOo`XYVdBb%DvIt^+!YRO~E@x53BQ zOBHWpY~)kNx2fY8Gjqo`ncv`7`x$HOxzL#-)4FRlPsFb4??1m5-`#3#7}$HG?8AyI z=KAko+fICiC3m8;kVoB%XHV&_O%zOU=dqdmu-xH?Q$|NmaQUG>_*hXtd~j__-azvO zO(}U(4gEOs27ij1q378tTF(ZaW#9-q=j;gb6!@MOz9es!;Y*P8XLoji_fhHchIE%l z9V~A~(f6v;;fIv-J<4|YA@yca*HWGxewbF5e>oVT93SmLrYu5e~>qV z#jP{DQ@$ZTm`nzp#vIR3QiipLy$Iypkdac`;p16{vukP zk{Y9pBk3|;9HM!4P2=fN?!626+jpKz=;iqA)S2WD!mPPF}@KYzYQ z-$~9m6^)+xqh@>={b39qf@{d0;x1WvAI&JhZ7-YYu4M>FETtH2fQe0~-E z%bo;>X4I~vKdW;ad--k=?I25Iv}xO^YBRhF+EM#m+Hu={U8?QfbCUBY)J7!l85|*|=7MKaJJZ;BY3ioaFrBU$aO2 z7IYTLgk8L|=$A4s*?ZN8Ml^q!tM@c8h8yUI2INYS(t$&JNIm^&8?-3>FuiZpciMZp z4LVD{IZ&zkKc>E@Q)Y4JpYfgiF3*DH=fI+vN?t$zkUBgYgLEfvZAr3DdYyDCM}NZZ zp!IO+Pti%}P3VQUQI>21(&6wum??We5IqQ5m@j*P>;%G->;lrGWE+Sgf29|(*z4%g zdLGI)0PVC(cad%2#3A$|Pq&>7%>PpxP?R}}S5I|D9r&O)wgJJXHh8vyeds#gT2&Zc zYQswpnD>?Kx0iS@rq9`tGnG4`)o#%#c$AI7@yVv12xa4!HaOS!>;l8tmmVUozR|gB ze_!Ly%hhk^qF+3^#cG$HQiNV(U3RL|ud*LWUst&u=p^3!{3!ZSF>D5|HTP%^9{sm) zLVH=l%#*KVtw;3q4KBf5$@)&uD=WKxdaZqPm@#?sVru&k`edouAiN2GJ+I8{ z>L#!HwdiBVXRXnd>EB`YoyTXny1C*tNMHXhbS?Uoj(!UIy69f?Ue2=xkFwEq&PG??hEB2dRs3S$ z(arGc2>MbCy(hj99eoyb08MKw7<6^*8;{?CzZ|xo#xi`sSifY!dA?#WY=-X|d~H}; za^ekG*sFGILbrkEZob1#loM;f22SiVfz9f#)>He<>aAV&>iBBk=dq{l&tuPQhDV!e zFBXZe?A0OprnJX5!8_TW zvpU)pXZ~S`&)2aa{q&i9MmT&hT z!5l(!hxPQ`5%6d1X3pFpMn6hVSdVPf+~Hwlz;);e(zP^ayv|AAZ!>x+rNduGU#q?t zyjNvhN4a5mg6fhE*vfNBWPzBO^RXP-dMKRch=;2WY3u*r zI91y`oHm1-lfdU4;PWGYQN``xlb9vMM48xYSO*gg%coC$CS7XleCadUox;7>f12y5 zz3Q8-^O48IsvK%;oc`8;69>wf(=(Qz!(LQOpTrrL9d|I70~TnyJ-*n;Mja4eSu?M) zsv5pBnmza*BQKn>iLAYWeFmH1L#@zYGxTkP_uAAv%|f@RMPCrV>V~eH9evI}Xy0f? zN7OirH5%>7-?RNy+6I3NGKMANclg+=Suvb!{k=1%6C9K=J`Nr{_%mS_-1B}-@rXj_;va|i4Q$k2`#3c3(Rz%& znd``aDV?Kuuf7o+g%?j*EIw0+4PLxv&EPuy>XeHN`A@=i)j34QBg=IU7Ci^E>90R}aqb!dn z#oOmjX9<6@(7yZ-T%-Hs2@C*ZQYIb-AYdiF3$G5SIVQYv$ zTOHqqhoO6{bKdWtr(Fr{#ZvDzH_(_0BF9AEl3g9>CE`@vbUx&m0d(6S5CyV!_<3Gl^OeEdak;S=dg!C)bK)E>zmwMS#oqi4xk;YRjr z(elUGukRR?2}ksH|6#vOQ2D+z-w4&=&CS&x5zeOj6LV&l|4!*FhG#7&wnpU{Uu;y_ zw^8m!c+hb85WR(Xud$`^q;E8yq}OVUg&1E!#+2qnLB`eB$gBNzA$XT>(*8Yg-d>~z z;8Xj1n9oH%zea)lJT8wn? z$&}~V3(&>2->YL5a+Z0<=332@EoTiz>%gBOdu4;YYYpQP`$oK$J~zmLCfm`!>+yfI z{hZ&uM*6I;aUXt*diUA|c49AZ<**L=*q@~TP3m0X^UvQli?u-fsXI;&we9`M%|+`O z*PGFmUnk~f99(r!K5IeXJwJdO}fqu5p zF12TXJd1g!xNM94*r;hs69197gRlI?fBAU8f&I5L7>mGv4D-eW-wXDS#$hia-^WKH z%dgEZ-f*o|d@^m>&VGa@&etX&Pu&{$&1>M!ZO;_iQ%rlN(4L7EE70@5r}p^V_IPdi zlzqe{e$%Kn;RH}8_}-vB-L$vF6b*|0%*p%bhQHnC%pU@A^rp{PgB4xMwyX6S>4Jmw`LE0wPhfv2 zG<#PYd*wuzZK8v$#_yx+g@GsjuP&XgLr)OyJP0b}EJ@@Z^5&KPMm{`q&&_nUlKjhlgE!%eM4q9g6g z+{t(mJ@wFrZpONFy(F}vv4_|Q2;XX-*S6~zFS75cA2n`1V$29XYI8Sqv0*-o&R|>#bX`d(`q=N}Dz_PTycG zm*!#z z?B2I@XYSj>eaX>g`I{Rz6_s0A6`TCUy*;em?jyY!AI(>N{@$(YkgGg5`AzRm(uk8& z@#YUV6@^&iX)rkz?3umD!1rGDW%ug4O+L!t&e*C5Yfs6T+PzEQL-x(FBKFExHeYmU z-|^rg>M!YQMi*@Mn|=;@nlByQti5LM+|g!X9{SG{Ypul{jP)lN*S|u)$mW~FNw=`A zm76^Fb(ywSUsl^wK3^OA-`dK|;cYSYqwV(j+qUsdh(6tcTnSpSf3iuB5O=4RF}cJy zs_|&ws6bHs2;O0tQGqyfXv4kvm1y72SB~y$e&u`IgMH=?Cl{f=Tmr8NQ0^F> zZ(;ts3Ezrijx=>|`^Qw|kWPOsI{K;$`s8!bV{+SM^JH%SOwAZGp~>fKtiP${cJ})$ zuZi7!dp+O3e&K~xOR)D$1x91C0ulTQ%d_m+OIRP63hz-`H)WLPl7_8)>KHdIsI)Pp z5d&&!o|_h9UqX2vY3zrdTHvN7@%bn(Agz$JLO0D;S|MrJPp3|C)8dmzn?f3UX{QF= zG^2b$(#lCIche#hNh>F98fnwqv~GM$%BPVwowVt0T2N`zNjr|THsh%$yJ>Oa@RXlS+8ojtqsrGo+En&FzfO#Z&>W>@ z2ZUqAn7hp9n}3$&oBu}GEG%Kq#ErS<5ui=wmPKea2MFbkgW!FYOGa9Y-2{?4?Q1X{jWQKK9balGa*f#+TB^P3VfO z@dq?73DLh=TYyiuZ}R0dUi>DwG=+g1to(o(^HNd#`(LYS%$84Bpz&??G-^ElI-7ND zX!BJotMRMPRkr<{*rb{K#?{+@<>(UlYIKS=*1$`%=wscLmW6HOO7A~=PFL_>ZxflGjp&VKZ!8d|@8$*vZ#vf=f#@WLZzp%8b27K>;7N3N6 zRbDsrsXowrxD7os6QBF}_x^pNm9$;$b=HDue{)*9H_%qeAhrEtaPSH3AFl1hvl?uB z{OTV#ZO0dBu4+z>VsI{>IM>p@Und z@vC9e{&V!m;qkM1&|dNO(K(m&A%mIcJRrIC?y;jT`35+X+*6!u;b$WDOyNefv*NNT zi>3EQup@W;8GR7Br?zT7`ChI0`s@6QlR|Ug!t@wGg}A_>Y-8pBFUG z=OgFp#uT(k76*T5gUkF*YZx&t@FbjWuP1fSe4mS&Tf`g8qp(~Sr!q}_@wq?yfo_l|6h=|$-|fr|I#F6H(fy9&EI3)ivEeCuQE4!Vf);Qw(^!G zeN#u%kLY#p;S+Ty{npDkjnQ|Dk#`UBd_HX{lkLxM0zV@i(`4W*(pU2SX}%M`?P%c6 z8p2%o=&8VR8Sl@qthVN3?5Z64dJXA2to&Zl#G6)5U^m!{-LlGojTBxFW~k zYe%_pr#!3oeaaayrZ>sAaf|&hoKY9w!I(g9e)Vh2t)X4fZv$5c*Zcla&fff%n0@-T z+U)k5DDyb3(OmF=@vSk_ep<7AdgpF``{uYIPfmL&c|%I?FsEP0|IXbx&byix`}7BQ zXSXlo+Xmj(Y&NGqxGuZB#@GE%5B`k!D`rCD9Au`Cd=K!AkM|Go-pBg~*5$N^k)_W6 zT7Ubg zmUryDZLUmzjC^{rxf_`bGv%Yx115iSFK+ecZS@ZE2L z<8<;Y=I*@r=d_RJ+oQ;H8u0&&Z}k1Q_+Q?-b-{V$n@@hXtekf0IJSpp<*TH=yISu8 zhEeVB+?99idf+IcoR9hDUGhBDde4GUtz8RpDF1riZ*6^K!8XCP&fos&U3r%YPj9#0 zy+CEfc-}_eHncvvpo{cJ`Tle6e@oT1Z5?=|u2alaGiuFMm#(C|Qqy?p3ciz`Q*Ih( z{G4*Wy8agng!9{}^S{XRzkp{G*S+LBo3x#+4=oVxfbZC=Xv>wfp>qAh;CuUmx2P+B z{f-5ul`>lYYe7Tny$g=~S>Ca&t^c{8@Mn3K{hjgz_p^Na40*q0t(`H~T6^i+X3dO~ z$X7w0TdsR}!T%v&1MPl^yp^pT3-o=McD-fRT&i+cT=x)l#23`JKD^**aJCMZ-U2rl z0Oze-($& zRpP&t3_GGR{vEsjeao~xZA{xu+%M;fGbjI$=NP)%!&{s+8S(e_m8{FmVO{1v_^8c! z&hxliJp1@B`M%|OG-pSa`2rmlvd($-2j+0@u+nQulg<^MVORN_G=G3Odf*p5ORBc~ zWl2>j@3MGz3eUBEe;`bn&US7G(4~SUP6{eS!IEFM5(A&)+_J?j^GQzI*Jcm!$8}ey`U4``_36(a|M8 zgr9zZo#wCDXWsSabiVIDyz?XMIJcq~#<8P(#QP6Z@8{8`C-p{V;v! zdqMcw-*KB8$OO>Oi zxO$0nJPZA<5dGtCUvqu?q?=4cm;FpO2ER4`6Y06=5YkO{q;!)4bQ7&RYCZcq=qB<( zJA2GT$ImI^=q3f|CPz5B$w$=RH-$~D<$b`(9k<)VwL zwq*GOU$yeu{)~>H*F^inK1SbMiXD}h7@fV%S}-B#$B9r!kY~ zt8X=Lc@=wx<}K>mPyLfCWGhm?ibut-UV*+@&?()MZ=8O;mVSMewH(b`2H`ChoR6j8 z{Dr#~IW}!AGG(ihJy-T@>B*uu(VlF&@*5@wYRY~+sXaOlo4qgf9GTQUFu~|NqyF$1 z?BEM*CvI+&;Abw~ht2qJT%U1$f~{*d?Ur9!;cJEMYhF9H{rrYX&g(J-vC-JWPYNqO zNF)0;>q^IT7ul_*xEuR;J$Q-H4`I@R*PG%XX)ja1?7pIR*~yylM-{)*cxXmnjr^O- zYr^z-WCD0~d|qR5{FWDclBE> zYb|q9Yu1DKZK!p8HPh(OQmF zi#_C>+g6gr+8J>8jotqk@t+hcbm?^V<1$ZQN_&+aJx=Q|TIaBVt@@P9C zQ=R@Z-8#$oZVPk$GQLxKDc|Ytedjwf_JVLJ_*Rj=a-7}2=aertuJS=sq=k65avc8D zr6#d=j@dwrrT+TO{`P2TR(sPVHxKLA{j6X26Jw~q=?c@oWUaYp32Wum+_h$|ShsCc z%m$VB;&`(`=c5@f_N}wt?T&}=y91Ni_X1x2)8WNdxo1}(7D>>*Q1~gq=JpP46fLN{ z0^keN#%9_WPPNf%8!=)kRF`POJL}EoGe4=ZC$-zBMGlC87%dIE?Zx*+_>AKJ?X*A7 zeGaDJ9n6=)JAI`%brl`Vi&uTqjYY&bsoOzpFxD<550)G5ihZOQN1~(1JOhuv^vK8p z*8Evd3oA|Q|5uV8ZF9Ofka&O_fj_;0QT^o9; zq#U(Z?M`oVCd`6&DBMJ@W?votkdf=Sv*$ZfI(K%AGi}tyNDAk*;|9mygj(?3K%QFg zul}fEJlf!H6>YWECffTLa7B35fBEC9>^S3JWkTcZ8Bw5gUc z^V(Qq;L*-gDp@0=-QBb^l7dCJSDUq8z-c%CyZP_Yv9H{v zkvpZpD*LwJ^8sJ_7)ysQ!npDLU`!>pG{4qihA3|u%TV846(OMI*PIKR{9N%~J^c|5AA6IxF37UWI8$zx5P zvG+0XZ2s}Qt~}&%J@{XWOo--UU!vZXopxgBq%bkSlZ z@Au0Q&&SP;3opI8lD$w_;%mUTsnWlE-KD4YF9W~Z&{bmepLp74=)PleN_T<3dAdsj z?;`ZUW@w{hGPDi7MxfJ8#xydos9Og9*Aq)W3BTJy-q-zE70A;5t>9gF^~y~zW2@h4 zU&ffw``4i9O{bT1F~??%WOc^X-_#Mu&ojh4I?9;a0KC$fn&FE}jMXc?;mS-`7x84k zQuf*_XyWR-|)+Ze2U6c<;$(LqFwj zK7sv%<9^RML1E&1*!^=U|97kdH(z^1vHUrnpq`Jeol^XSnKVDgHi-*(ANSdbC-5(l z4frmfNqh%?pOst@<~zTlTw-&~|G|SipXk!=4}RhJ>^VBy@x`w0_VEm}A(J;mG*6P> z40ykPoY}Bub2wq4r^bD;#5czLt|-d7&Nj-2eu+;O?ho=D{SosA{GT)ieYAVSxZf3R z%V8fc{l1dEX{ZSIPc^2$oc`8%3#&P2RE5E}Z1y{4F|Su2?pT6O35}FNe_QZN@zW-Yerg!= zc#+QPn1i2DXw2>+|J`^AMptrNdOH*Kh5{cBpd|1ErZmyUU?XfxyM zwU?LmwSuSh@2%atH8-n`d3W1V#l+6D6I*KWxtr|2XUhtc*vJE? zPiIo&KNk@F2!Fz{CwDZKUk6@&D}Gc94Bl84|B-L#NaJ1gWR~gS%F*poI@1#Pa|GGC z1bt*D@Jct5Yz#fdDMRQ+F~JJYMIUVp?THmd50DeP@BS;k&wgMNjM4>Lq!WT;>0dQm zYO@FL0rJAr*-|>uSK*=6Hh$u1y6kYe4f`&k$NbLdBhneH#v6cJRWW=&Iaagks`bQC}_GQ%l=D}KGFEv z;@AN2>!JKI@G5*NpQZAtCrCd>RZmJ5svTZGONWzBk?IjV4^hWTcosC*Yk}to&)0^( z*r@v+?$lY?F<9rs1L{=y($PKo6bx#M;Fb-kkMB7{wii9CZn!d}b9yu&m}KL!Xuo(D zwD+*VMnTXn^vS91LB^V%P2N%M!7-`-4;t0T zSrO(o;Kjww2z^jNAK+W{oc)E)1L+L<%=Sn<_4HK~JA~%M>3yX>u66bs9N5P$u6}tBW3gBKUb0v?lbnXZ zJN+vBO`?tR=Y9-+Rc*3*kAZi6h@JHaU}U{^!f$~E#kOFx1OG$^e!+eov=o7sqQGYZ zca_1u$^Tiv905+juQEkrf_W2tI}e%>4K;u#^}XBfmKQeovYv^7Gn>6R!dILzBpPtr zbs!!6k$&msU8^a0<}vDl7TbP99S(hYvan2a=ANPC(Q-Pycs8dV_T`I4M3-jI*Bsh# zXeKzlJt#U7{p^|19z+%gk(K77x&6T@ZhGZ3?s?POt8;l5WUga8y4vm7IUTyf#_7ok zjiUy}4*l=iouDy|WADA@Z}AzX|M#LheyOp($QL`%PAXb2s0=%C8-#zA2`(4Kz=iUP zuX$w-z8g2fy9o!s8*Q8Rf0;ncHu~@Saqtv;AbLo9)r{)R;{TsmldiU`Ue*-)-(X#O z0J`brUP79DKXvYq)~&0pEXT)li8ZSCJ)TR@IpyowVEHSSSh>BATiF%8q_dtg8h`lS z&6cl1KBx8giZ)wTMFT#jVSGrd@fnTaSGL4Dtal&zUu6xahdb{oI*4J^Gp1%QbG&Cl zS>}o$K4dkMViWeE=lO#-E~=Rlo8Yt1uLAKaeDU^0W&Cf+j$cvD9LGnVZv5Ixl|Ktz z;gnk!`IJ8!dk6V<-Fe-jGX6XHv7`9NvlCx7pYrFT4>WFCw5yo>hnYkf`CEQ^$D%U+ z*HiujoaN*r&kcviCj1Gxe0VUn=w0rA#*gAg-t}VR>>=KN44;ah;FC7*<7@YRo%Ph6 zr1|l$@H0M+Pd&pM79GuhXln3?8xTCq-%V|&DU7Pb6D>v<7#Ei zqk-%7muGbO|IduBGJo5I>#oTuzTp~w@liZKcFnanuM64Z{k#t%yMKf)<0-4{@!Pf* zk1zWPcYS~Tr15WDSka|&9-nS6^nbsiYujnX3)dZHkDto_hTBTU?>LM-%j7ZemLCW4 zQP^XTuRU$n_>c*Y*LR`3W5&OB;f$^)&b1eo@$COT|9^7g!gXWqg-7xK;yq^J7~ZGz z)UotcfHQXf>>pb(ntu3z`+M}Gd_iw!d}c#OSbpiLB?}xg|xoXa}$7hokqOH{??=KCPgD2Xxt-xMbL%Y@9R@%4i z!r(#+JtG~aJR<=XuZqEG(^y66MQjaJ6c9Qy7kpm{g+y$+6AHKDfo!^gCJ%Mk_Til=GGT0TCoqti+Cj6}4U@b2Dsby7< z?E@1kdteoo3|*j-pM$a4hh~lNVOK z`Q!yv$tNRK+rC@4cw64Y30gbcHfC;xDJY&0H>M(Zlv(}2wbtU}@Z;}AF1?HX^NxQ) zXD+xtcdA_#;W~=z9DEP1=DOp&)m@46B3+N3cXijD=Uv%#mH&vqRMJ1=d82=F;CG}Y zdH)FhSFez_3;(Td>;~KHje*~i_JsY@z$Z^$>A=+Y$9>rq_y<-PJG&xy^|Y>B{K-muxsBHovn3B) zakZPzn)WVYzXX1VZ7aBrAD!1W^P^EG3ZJ4!;ZwL29{)e%&}Xh%@9Z0}p@lHC!1`&k8)wn9t%K z&$0t$Pc>APKee{~^~E=U>*f?)>FlvGXtQs%0(zdH!E< z{)Jt8c>aqSANY6P+xP|Etu*|-kH?qb&-ewtWv>fN@MQ&F%Q_{n2fu=UC+$f)7WfHy zaxuY9F zk6F+o_y3o@H-WCQy8iyp9Wsy`AR$bKNbXG-BU+FtaVSqNqoP4VRJ8t@dlNu3P6$@0 zDEC4fV_TzCtonbsVN$48s?a8_Eddp&s73Lob|e93v`TRxZ8h)b``qUt7m96v|Mjl_ zTJKu_yVhRkx##RN?X%B5`<%1SIX9Iwp9?>Bdb8@Lapnx0*_pDNZG#`ne1{9S$+^?dH;&|-ayR7im&{2r z;Yq`K6BD1AmACk3eaP7j5ORaD*tQGM{6%MczmLB36d<{! zJ~wDUQjUvHCHFGm$MwKYhc6|=v3(x1$z(V5!A_~rWF35g?=R3dw{hml)?e}a+}25hS!Bw1V@?>3UaR%c9;wlZr>mJTcKNm z@2$^x8tISM>f01s=e2oPpSOZ>qtMf{jx)#&>rMBDoxr;QcR*n$-LqsXb}7@zr;jmZ zg`Pm&ZvNA$$_~oSG-chgP1!k|tK5qH)%0%QD|E# zI(YmK3<6K?FmOhgvfuDd-&W2oOwR>xq=U!*z!>o4mV$SdDZBHp;Bl5=dLQsC2ao@O zN#Mzy2Hts;54pFM^9|Ea0`CF`kN<%x@Z`=0?=paMzgs!;F#TlkA`Tw^19QNWJ0F~@ zC?EW}m2(i&L*QNK;PF4O2t2uq!COlCJ_0WY-eB-z4j%sl_29|91H8K_A8)~I9R%KR z@a}Q&_#apap4O2zkMOeUUf_S$Yo_c2 zxo?8^M}RXJ>p7QU>r!o|>^Z*E-ZEux%YBdV4^7!%-kZvyuj=f$VgAj0_h1~IJ9Xau z`kAvqyg@ZKZdt68t!qq)2EpAVk!UH#!u^JR=ONnRwXjjfN?2Tj#=^mXZ? zwqEM!KYv9IyaKrT4`bjK={l3WR#^~`9yIT-)wV8U>oFQXRw(nz=Qxc4S$^G)6n@@hp!zFi14InN{=3VD$GQ|5pH?DOCA zmYhpq57oH@8E*>^xCF%CZ{S9%Zxnblv7@OjA zPaaxvY2m1fJkq_0bc?{T%TL@V2R>PmLs@rIo{6NZG`~sw?UYq^*YA;TeZi=TyVK6T z)==)t2wO=RLwqkJ>{o?DEA~;Q;gszU#P7%dJ|~Z=_#OF9JNXyP(??aL4_sIA0`=SM zKl|D`(y1KyScNGVTCu)xOGOBGA?ZCp8ee6ceQhyieQV&>iba(7PSQ97KU1)#BJ<=Q zS5#g!a>lvD-&ueya=}v-eFVjXU6 z!N!U(?xWZxn}Q9*T}QdrR3td_8VAC_Fl6M%zCg)7#*7a-mhC;?o7FG^=$X~C;YwU4 zmsO9TLum|);RwX_RF%dZ;d*2uBb-Rz~ zM<3Fk4${Xoc3)22R8Q%Vs^b;M)p5iBppI$ZP3mv^ki-A3uClLr;!M`WgTDV3v~ihK zDs=je)A_OGOheb#yf6>>hwF}#aX7MZ7&=m0T5@i9I&;HZ zV6&quwX!by9J;dRFhKx0IWrS?IP-CXo~t>~qv*Pljc@O|qExa`^T79*6KXD)DnAp_ z&uQbf!DjI_$jxi8fBFbHl}h6qpzWK%+w$(meT{ruaCx-5*L-NZ!P`3^etJE%P0`D?0xyyVL2M=SH`Fb@1XVjznnV_4DhF7}Krc5p>m;*qhnFn44}) znc@5^=UorRvC$oF$M05m6=Sg0I%-)%t2Wqz(if9D`!6@1;xn67H;u(;2k&rh`(0mp$sXcw zGdXor=U-l$k!i{@`~WmUKd!o9+I6MRXPVrc-lk0FgL86BSx&Yo8_pg0!r#N$UB%4^ z5~l#@kIu!t3U<64;*D_P4I|zlQ>Ob~?0B5zwd0i%XACeBoeq0RJ053xb0#_QEaIJI z$~Iz;Y{%nluO06K;+zLeLuX`7){e&+-<->wc(aLDWy;=1*4gm}Iq~KbXATgdeuUZa zh7oU(6Yo0WT}Ay^6SL!S_SeoYMx3RfpDg zq3k7L!!NzFowVHj9r1$hV!?fN>il`7@I#~8(N5n_`eRJ#H27TU$)@enGrn3i!sTbq zKX3-zo{NgFW90J_hp%-G@&ouT%Ogs-1G%GW*WEcp3J_}X^i zYuiOGXg+9vgKHI8=NZrH^NMXA?+%aWG#Ku@dWUuE_u%nYY#z_8o5r03nb0N^x?~z? zlMVmS7iLadP`V2~&+RX|W!B~PHf6avgclG_KTy0^Y+lc;%N$1BK|ql35roqh?D+6| zZmAQ04Dm-2eiq^M2Rpva^TeM<+)02%_<4lWC+zs}eeP^0eiiXAApA1I=@)ifi;%_1TX2PE)oIYX4xBCR~cME zoc<9aPxoAi`o}V-e@vr)Xg-$7yi5HglX;k(AM>&d=3$w3Kk0@!dS}0T%DU!ahF@2o z`Q^sm&b&b9f^5ST3s9G zNvARo3NsH1vi72}q!)7p`y2e(%nNed|AbYd$NQLP^u<4yxq$F8nYZNNzvH4|Ge+Z| zj{hkBm-BxgbA;N0F%{p&%{+O0#eXxen2Z0-{BLC*GKlZ9_?`;R{DMa-`lHkP@Ed^N z5d1m|$5;FrScBg$@LP}HulVm_{&5oXje7n+%>T@jn=2~Q&%XBEf^8MA<0{Xa@ZZV& zAk6ore6KI~RmDQWLi{h~e|^Ez6{GmSv*5{!cbO|>UZ}akFDr8J3p1y95_diGj8H*y zg<bkVthMC=R8)ERQcZgN0^HB1i3U5q?Hw^P}%_F3zW#X6a=x!e zT+#~9tjP|SSQh&YydAs?y#<|SCeT=i{&8-%ZVjh;%sBDh*YX_tm&Tg?%;yL528rFD z7;oJEB>S}Y0-7TvZ3=WAkaK9Ss?7gy%W5t8Ml1JntphJ(+?B$!=KO(6ZFGY`)uf_h6sCbl);{FeI0EQ^v4hBkJ;Sc z{{cE3%UV?Trm?QlKHyeM7Cp*u+9XUdlrrJ zbc7<`Z1kenv$ofvJBGT!`%%vL$foamjDQ}$Jn<~pV#R>HZC;Vj$Wj4Po&(-DEEB!^4=o-`qsXUE2hN|V%_~0z20i6A>A6% z4Pk$)`&ts{Y_ic0qMONvKg2r>cDbx{JD-J4xl|_EL#d4V{;kq;zW4L((t88ncKne8?D)0r z|F)j?|7!j>j5jM2$gm*i!Oi9=yoqW$F2VoSRR5f*w*M^r+o6+qEA+`3jd5@v{=9S5 z`EI(_I6GZTD3bY_VfO#Y$#{G0c-Qr{<9(la6Y#GipCGoc2V5_Fszssj2?0-EIHx;6D_9Pmr@8vR7*x)EG4E&FVYnbo<;!avq;NYvDys2v>He zvL%~{?SL2DF!73rr|$_PO-q=#6FB!(guQYRG%qT3?EQuuYgdkK-*Gmo$@3DP&B=G~ zWOt}NJM{zQ*T$TQG-v8gv-j9Xi#}M6{XJ(C7~5sX|HeqpZ2aES62%reaz>Z`EBG(l zkN=bS|Jh^9qnGFhi}$BDa+gx&&yfMWu>qOO8Q$etjEw`ipQwk`vSJ82YUYmo<>O4t zg-bmHCNA|2h+{+lL+t5SggJY7k9R;JvdwUgCpLw%Zauh*x`;lvpM0=!U*q)#40i5G zoOr&@hsDk{E$7kqwsAIkEo);}!_N`$myu5Ng&K##)qrvA2lz88n$Jb1sCCm zJy`BblA$jYHEiqQ_%W|K)%D~2q>aa1O80Tuczqo|=1(6XZ*9MP$B#MF`K}-L&)adC zCq2%b!p19d{Fobccm0MW{mdZB$J`=9dHYhfsO<8gZv^^s50!lf=|1k>uVe2|b-$W? z7X6vCBPG~scQaeme_tZaMf;O}<-FM=8Gx=;wdkFsUnmzp+WC9rv*-i#DdHx0Tj%(* z2~o$!Jp^wCv1!!*2=>^zo1uvDH^g`hJ;j^0ou}??YvHwVB*O2;WWNoIpAKqMZIveikr^_XjNO3s?FFsQqhm>hmW~pk(kQxD|n zf0WiWDfL)%TzYrVU)=P9yjS#}lAfQNUIxj*K9akR4R#a!D0$e z5|WR^Z9=x8SB{Awn+^OK`?7P4+b?RdGfmZvbiPgFvf{RK)**2^dJ1c;;=4xP!_}Dj zC&G1RL+2w{Z>#H$t`+&2X;EBTuOe>j@2fcjY3lBvZ%3(%zR#iWE8pdWDZL`vpa|Ra zqT||t{@*fzbFt{aN6#Irz1orC$G6v9`tCMl?g;GKiYVJQ`dH0_o|c;bg^5j#l@6Qaz}B6r!lF9 z@ke=Mdt9gc9YTo}`C1nbC+hQ~PX63sTi1YI-j8ytTsN*smg!XZSY;{&kXgNz-l`$S z9^+zJG5fBE8fd2RPUWo||KN>ITpN9Vf-jne!XkP6tJR+S;^8ClNrRr8r+V=QL+JEDWTVw+?MYc$9 z)tQ)6;4__bYtz|DWN{&KMeQv9h@Ng*wlRiEPf~l=p5UH|tHeXZ6MuS$d&t2JJ9C7= z;tlA|o@1rbkld2%_EXWmo;Zf^2z1r^EdMw4rC;+t@c-YJ#Hat?_oXTD^S|m#-&FVs z`ckRMot~axPG98c>C;$WRXe8Y=^veXVm^9-$Z-;C;IVv$zt?l_pJ;c`mrDV*o%H#gMPd`^O^iP=*Ma3AD%JjprycBrX>yC z*n^H7zQ;G98oGyhpU^uES&x1!-B@LIb@~5Pr~NN`Eco?CkH*)`l(j8qPV4HkYF(>t$#oW*GwAA)Nr}djdE3v_haA!B`dAhgr z(Ea>3%!Tzo#<%>;DV~-feef7R)>6%xoLO@G;|?zK{eb*q?3Mo$K5qg=oV10%khFhD zK6V_!+R=?doCj{>{$c4r^!qVmDf4K`@5PmWlh-PHt*CdvGXWh2TKEdXdZ*%*iHzac z%{5MBj<&MM%=B~Dz$9qqKNA;v`6uAQ2lcFVxo3~f(4>yaxvQ}Tya@WQA)eLCjP;P8 z&(F8y^@ zJvFSAdp*QY;;o~6;_yVAwYOB<2=f6KS9Cs}SGKd}mWtm-JGl7QIr)kon1_uu(5ItV zHrbrvbhg z%Q}YmZn=l^EnZRE-%b6bgL>gNwXf#&S~n;k#9P{&YZ{HLP+e3f)$K&}v5s`*W8l$| zq$l2+$eikU-qYHkFM~7d=+l$hmh4sd^4{Lv%G;GkT5DJN%tNMQHuyT5syp!~8ehP* zbJd(s=W@HDf1?YRO&G>~O?1$CP<5bYPv% zE<-zf!dgXH(~-`xO2@O9H5B4{K1nOn`e8hu?@!ap;sbGy;1*eB%ZFHHA^L#s3H2D? zl6VPzr=|G$eM^|bRB3E^i+j90*n6zyt^vs-|0k2}aw*LJDc_R!e7p2qi(GW+KIgBK z%T%Vh-ib?8mpSPZm(2Toa@iE(NFGc^HcX-&=KgJR*<{AOx3glElgUGQ#b}%OIIGM{ z{MhMMnbHl)F0R1LQvdQU|1Gc$~gt7g90%DVd^Xyt`|8oQP6iqlx*DLIyQnjhJ` zz&%~!1>vf^!guSSyYGdw!og9v8>aOag+22x;@5$rIFnM!BHGnc54TP0shen*nzri6 znwwqMU8Ld8-E`+I>}fG>>y-pR23LPhrRVtE?(#40)<@s(yOMNHX+9_%2=S z^&#kDuMa^ZogI0KxX96tz0k#@v75T}X6)8mlcEbe)LZ=0eYb$)f zbQC^jS_%uiTAyl}z*wQZkO}psqt&N-G1hJLa1VfFwRAnbAKu^LWn^`S-iS+M>{1)r z_m^_dQPV!t;b+W93z;o3==2o4-5YroJEKciTMFGCf_^k-z_2$cIoh4KWtt9n8|fP* zO|wkLKaAPxvGugRcDuC=G-C#kZUOo2piVyI%_7ctp?|!hx7ySns^|x*yTZ5d)|FfD zr-Gc((c5Fz zkQ990@^by#QvG?0%k_^9O~GH*EJt|x!N>NoOM&o%+GxOl6Aa2w(iGP3+s+0&oW-9oz|_kTGal}GKS3Zc?0Vn zvRa;H{8+aNm$}or`?v#%aphU&Q0sUn@Y!kHFG*WBJ;B;PZTu|l`v&d&JNUE>xudqW z(`~Tq@U>5%gA=}vcf%y3*R8-M?{&*?$#dOJxXSNFT=JVjUh5XJ??FD#kk2IY6%VzN z_BQVIJxF`3YQfcQ^tcbWhZ5tGM{D2nkc)>b*Ct&`SZ)|-VIkRWYKE_03 zjARRIvIpawaXcy$-8+95PGyuS^NUVC{=!%FU|6|H{5`M&KAKdd24>+w5Sf7QCH z{FSc8s>huC#W(tI(82SNE0SB{Ysm=DzRs#+%FkYYKHD{>Nj} zjeT|kTx5}O+y8JZZt&9+;3A)dTYemF>t9cRi_8*k)9;T>H*)9%xX3Z#dXB?2pPT>} z*(TiR?~YA3@s|_eBJYH|{mo->t&dKCiwqQQ=s4WAgD1d6E($mP#$jSECkHrnXe*#?Or`ot2+*BTGeYXpj`y={^XMOg(zwa^g{y!6z8qd7b6^}Q4 z`q|?zWef5x9{>B^&Q&q=kWZX6=cS}MguWqO%%xBK_O!0M4wLs6noXu4+~ThBNC(he zhO?MswxaJuimVnZV8&huPbbisnkcW8ZMHD}9U2c^5{xAw23RJd<)7M zOFE@f;a*hewRJV6DZM6g8gkEXMn#Z&Vb)!A&l)zgRn=Gd7GRTJ^%C~g)ld5te8{*{ z9rG=Cfj42Quk$U4F-})6LQf>m)bhkGGh2$1`5(FnJ*4e2Q>Ahs$JB1Gptl9~VTaI$ zepybNCf8Z=kD>SB|1w*2?)v^@oI|>2AwC^_#nvnHlQIN7eGB!M?s$>c^sXM`TaZOr zs&mi}spt8~vg$#;1qQ$AjLFp__{M#abC4lqW*g`DHBJuwd*`YIYZ;6=B}=ByE{mw$=e=?1|*4bj~ktO5UGX9q}z_<6WfP z*fUqpp==iJeBXj7dYMIj7t_Xuwa(9|qvj%Qld12NdY*9L)GdG)*~p7bs*{2O53dtB=?)Gcawf*0)Ba{DL_N(VA9Vz+f<>m zlUz9|8EL>3->k9k^UmKip7f>1$Zs4xk;+p~*m6E!b3pdr zW0id=N31Pbj*khe&PH~^(~6@qOD|f^eNS$BE^n%y2ZVowda{02&VRd(1xdZB?N7-x ze-DmiLOAA|>3y-Np*)yOH%R-n{xOO33OL8)#~R{W4qa8wBH+Ug==+Bx{&V0fuUcrH z_u};f>$H?=r?0ec$ix8)#+yBf;FGkpA7B zy|_N;mOrPxHK&Y{p7K#yBX4w$QMyY>U;I&|IVEY?b*6mM=O5@{dOaX|LHm`r`16FP zd{)v1`mTr9)h7#kV&BIeNB^ET@15=I-*D39R>zM9nl0QZ`07gTX(_;lOl4o5VGi|` ztYmysI`VVNZcN`m(`{2bJatXY{qxi({fB}#g%)cqa|(Ht`n-*;OYQM-!BzC*eB_b* zzu~i%lua~^rxjN(R@qOTSiQKM@@pP7qPBYRDJ!ZMm#nQ`Jp7sJ#lsF%FWxl`+fwTE zGIe-~e)uANa5r|KrN4=NN+|10-LyUa2ywY#L{S%ta)7ngDB7TA6s%x;U-O z`W8?P)B=|Sb-=a2Lf}T=rnIu^ytK0N`*8c=uEHIF`w(s+?rPj(+_ktvaUa1w1^36e z-@ttWcQo!(xZ`nuj(aBVFL5W3{xV<%a68Zd`~Wx%e34dW%gSxwO$7fL+^M+F<4(uj zjXMMPx41WYTB^AxxcUdUHvxZ7D+~L4OTrnxCDpg{zc{@tQbahi^#FI&y7XF4pE#ag zuKacPMqC+eK`&RINb-xv<`=EIia++-wlMjdN>gN+D|=1{r#(fViv6Nh@9i=au{EHy4qn+A~$2UqqeVbwHQ@9wRN4MQ^Ss4Rpf}SBKcr(z!LY z43VF8m0>6Ggg@|=&Q<1ey=87Tsc&~XuH3C#I>$&qQrRi9t$UO|%^kWabxSYT%wZ1Ft=Y(mG%cp+8nyEW)(jM36=!OyM zZZer8>CQ0LVmZ4~vdC|8wU5!xJZ;4YtL4mIW@Vc6OxF0h18Zg>Z&%CaP&lI)*I8>G zdxO2sjQ!$;W~B$6Utvw?{f&&BvYR}B-C~0MD_7@t?;v*HKhqp;0PDY+<7rN(y?m`@ zExtXumaDb-G04=h^pn}xIrq!6S{CwFS>K*k$LrzI4V|ouor8|0eZ?`z_Jw)a0kRLg zh_hA-n@@Q&sl%td$B>|}(noV!`|2&dezv|n5q(+TKIs^{bw|3{dOP^-lp#oeZ0+pa zT53#(ucy`GA?}i+P{uBzjTqex0W!=PA^q ztkCbV^=*3}@*d9uY{9E$Ipy8Kex_UI8)>^5_PV~VoF8(2=iik{Y3=v*D>>jRDA{nN zbJb4DqH+cq-(neNtob)}+QPoZQ;f-yS(=Y&FRBgt%5Fe1Nq)8LF)hUw_gQpFi}ol) zPs9$o%Af~Eb_W~!J!`dedJF0%vM>C_y$|QR^~)n~m93UBH_RE-2xXG3h|=iG+0sJV z#1xydf}W#I*Fo*Azv=84ZXXzZhcahn>!H+BrhoUx;p+;Ev!p?OmL*ZgYd_6WEGg zy=@$8zS!N!#_Uwq#E-D{`xNnG#4ltXnyB^I>niX}BWpudvp5rBhcO>bT;3IS1?xBq z(U0vgbmv4}S6B?)_^ARXj5%xK+O9D5{locASU_A~=pUndIbo*|c6(P?gg&w`#|ire zVGUhjF?i;w9!}V3!hX;dmS7%#m^CyzzcUHDuPdw_yC&>7>@ev)qH$uCrz}K2+Uo5; zeFCzvmA0FJY>aa5sE~Z`L>4D%%WYZIMp!ZOr`g*-kd!|s+3PTiB{SizBEnqW`qXPW z)W(OIZ=Vl+ifoy8(x#@Ay<+k9>+sZ;@m9xn_-Hk2As+HLMs^m9r;~H@S$@{yu*t~F zG_S0IPsZ@h@|(;V@;-L;u9NI}8T1(=z7(G>fUh-9OOCO|anwJRUz<3?oE*!Y_{>Mz ziQh*28N^rLk{!RwEBT;2Q}InFI-8SUg_B(5PT{#UmaXCE980w*|6Lowe)0?81T-$$jky{)|08;l7?_ zdc7X@wA5dY_T?0tgs)3y;@703{FZ;gx(@wi5o1*|?V#}$8drLY|Ajs4 z5Zj)0k1vOJ&ts#H+OuNcx+mP0r*qpKUe0fZ%<#sr>2KS&>VF~a<|!~8;kL}i!dBWT zGl}OJgx{Y0M$a(Q;TZvp1jYcRz*&F=Oai6>=K&W0RlsHFBD{0COz&JS^#Z<)-XLkt z#{WU!C%n-J-3x=PIblN~8;e40C=REL=U?8mZQ}uWNjzuJgXW@tn~}^1V%V`*MF+!) z7-uidm}>0f9;N*y%cHnE;M*Yj6>HD;6mpMF{RM%>-!OjEz3;XbTHrfFEWICRAOtn?Ho9q?6# zdgvELZ(j#}*Fal0yn00P+>t%@Fy<_$+^^Ekb{xvAwLj57`HBW<)L-QqMEuWN})|Gg?{dmE{QZtT}bPdoNp z@6j`v+z@-0j@~LJXUql(DvxRb2=SvT) zao&m&uZIbfUzBkv${N@T;)L;!vA(~IxV4vBEt7h$&JXt2x|9h73wURNF|U?$MYUs+ zHbAwvC2_;xSq_f+sH-=6OUpZy*XmyAj81+vP8rKn%HWnO)lWKPFW8bNK zh%mPey|kC~?mXIYIq^Nz3meX4+dwbFyg9;t$+3ORc~iF3Ig8ERsp5esXSzJ>UD@vt z!t-9}V)GT}=gN`oseBbZm-<(*zM1W*tKqC&VWy{U4R+PcQaj2E=Smr>4aMZU>f%$(8F+Guz5RLVG0?{4FA2hozfnSIReJG_|!*_^@U%v#&hv;hY=zr76qHeW_b7G=+3o{UX3xSqO$ z^cfF0%lK~K+bk}erS*dD%&%LKLpx)hM!iiEXDvW)K(82L*=O-$*myIa%dHugJg;QP zLuc>Q|8@3G=j=v>l4pbB*nuw(U)CI?eC|0t8!zRo-Vot(?oRbD&H5@Y%X_j$3@uc*I3RS8a3Q;;ohwgB|qJ#z_`&b9*-W{Qhm(jja{zu?5(a{4BO(1ML zYvca;R*Mh1tme*)a{haFQ3vB|gs<$lypvcW+ZR9lwVF61pqrn5wwmzm%#Xb{0#aev^0)f0+e-T>r6ukN5JIMJ__W)PMS2ysy73cA=@- zuK(~&JfHuBx9dMLC_d0%ragPfGW3=O@gjd&TP6AH|1ka!@s}C?m+Sus{+Ia6f-}iq z|3~uwH1fZI{PiEbC4M^j&me#OXH1We^Owad$e;g317nr(GyG+e0sdgD5?!c-zCQh3 z!VF=za-(f9+g~x|Vw1D+Qj@ceyUga|$2*6Oo4#do);j-Jczq2&zRcvTasK=3*Hz4C zO{vNG@2g*1(P^0l8=U{%`ZX1gTxoJVyd8A=uxO9Vhea!%CG55;lapS*p<*I_#rWCj zRhpbnhJ|`uH!M`~3^?zQ|9afP_$wdfq4eI?okQf4R=>XD72?k%{!YS_$4+oJ`{QTr zP<*~0A$%y|&pH2<=jf79kHh5ANZ10xwmaz-a|iPj;was2NYbIa6;trf0$*jE$DM9Zf`2;rA^cU|$+&kBXD)Si^Q^`1AbwLxV`xdJLg`Mz zPvyIUJK@~L#vt08>%%w#?}+;8>@{y!z3DE`k=&POQkbA&6;+X$OS*ksZl zO!|Ruo1FQ`rg^QNM&&h__*YzFax&^4Iobx>p?#G4sIAj3Kz@UBF1(<&(SOl@GOo&` zyTIr2t-RFMxALuY50mdg@>RPjJh%R*72+j@LqoeQRL9Lu`1O?UujHxvD&NWAiH@U7 zqCNJKj`ALiUk-0ax%pG)9?-Eu zBptPr>Z5R#V>mQaS@d6JSwz~sNn2%6*;S{J#JR%B<7cFEzf&Hyb&1mkU*P`+{%Y4O z{D$J5Lz;(4Z!hVMr))P8J`w-BNNWi0MZ_70Uzl&zS#7H}Qn}_DZ==d{1$Q^89EV8f z719wMu8esbRd?077wy)IwEs#v|LySaBYbb++vSVV;D|TghW2hBoY(4YoQc272m7h- zT*j}V=bN18h>Ki1d!FTMY{x$Xzqv7A<2!sSTzMbh`+2@qhPU~Cmv5ExE#X&~oSD>Z z6y;EPmA1lFFQs?;^dznKfO8!<^obrBB_TUar7iwex*G_m4t5%o_#VW!${VL1DqlHs zn)Rer+v;mndp*au_$KYVq)c;ZWT4|5;*&;25v~_EMmiShL`Y{f_e+G(?=57cg&ws1 zg;1%+ha&XeR@N1EyiioCF`|fb2A2DuF`|h3by_uVR5)Wq5qImbN0I!`7*WK%I<2fL z*x`&3Mg0kP|1(Au6%dZTW`{FI6a@))|1(Au4I-R6+Tn~5MZ*Yp|1(AujUb%y-417r zC>lw)`=2qQXbj=#F?M(umj8n<~JsJn`Kl-}t zk9%`&HTG(M{zvXY$GkVzVwQRyDzt(J+epWrTd>BYaXNMUkD_lH(%Z(IVY}{TL)VP- zWd4S(DIF`qoy}2n)5JjTdg#U4XJ6A1$DS<0oGb?ZvRjzrF9&WzH~cnu#Esmmc>;PW zYo0;UR(M2s;4G^&wjT0wAM{7&O9|49gwbP|d#Y^j>pgS%laD>u2n_iYvH#4ya4{r{SvGj*kw}LG}ma)Sj}Eb-lpm>IFzkb^Hp>a`Gq*w6V#lOxc2`X zXAZ?2l=Y@9QPSAXJXhf%(v33bi;^#EQ)8p(^R*lJW==5YHr}qzCmdR{&egG+d?!-p zC;(q}ggEDn&e0NvKUsSLH>sD&M!?;FJf4Oz&b=iDCg<5ITh51_=tOy&s&aUPn*8)V zBipQ$dxt}p235Rif~S zzC-$+lGN2B@Tt}WAX};uZT+M%v{%0q3 z_s3WV9duPvcQ4oXi^=bH)zt#DZsH3f0bKXkx z^bmEA!o$`FonytTZGY|Dav?Zt=$F#l?uABSbT#R7Kj6EDZ~5QN_j2d^PQKST-?#C- z!};d@o__oJ4iPuTx7SJYX8Akc-{m{(eBa1-jq|;T@8!<-0={=R-`DV6JSb)g?>&3qtrQ2$il9Kd%WcMuwW9>5Fu06)OKyGaK)Cu-QgHr)W`+Z=K+sOiRZ z2YLWKfgFGf;7uU;*Vo z7?=o50wx1ffT_SV;2hvwU^;Lfa6V80%m6L`W&)MKET9Uw5V#1q7?=%Q0$d7w3%CsU zFW}oiH4p)!Kn+j}%mL;C^MK2N`M?#xl|UVE6>v3h4R9@R9k2kn9#{x00&W1l1KbEK z29^Nd1#SYC0yhJ<0Ly?F5C@h6D}Z|7R^T?^cHj=+PT($}0k|9Z9`JqO2f#f*BXBQp zAFvYmA#gvi3U~nc5%3`J5b!YY{{a6BSPdkACSVP)7FY+Y2Q~nY0FMG2fla`VfyaQy zfu8_R0GolI0#5=@0b78rz|Vl61I<7S@C)FVz|+95fL{aKfL35TumgAocoujL*a;irRya4P5UIbnOUIu;(>;YZ@UIq37uL1uJybinpyb1gc_&x9k;E%wcfIkEKfKPx! zz+ZvGKpW5w><112Zvk%u?*Q)t?*Z=v9{>k|4}p(>zW^Ttp8}r&p95b2M}Q9CZ@}My ze*j+sUjdzfXC&(oceBQMfoYjA4LRZKdIvnY-|yYz1!t|XTGn%>c?#$=XpPO@~1I=MY%IK+}|`# zaqiCbb5HR$)9v89mqeTQ@)lzg?!6`>a2wy)uLnZB&AM>c^`-Z6_u;HWVQIZd4_wdx z%FTtPb9T)soy5Jpe(p8Coj7rD6h3d)<)u@&vp2dcT3XIMy;1PWxuZA48ebD{LFM#F zYf#!&+&S8Px2c34(f95UM|8m;H^s{&CgMu1g_rli0d2Cs(j1I%i@k; zzt@yScs~Roj2kUQ_?8`JpAZ^0s(m_FKuhP1&>Zqihn~7~J%e{v208S+hX1?KaYRqv z-s*S}{pY0u-VTM1cLKwJ+j*1aU3+b|=O*07x4Gxp4?R!6Vh(Q<(yejp@qcZQRl3?_ z99(nB_04h0{mLiRr4ih)=!ai9^c=?-htr|$SZ`h+#`ki*@8f#`Z?i<9t?DxhTbf0@ z(K3qnIIrKe(0&iGW>-zAz2>TSe0o%sgjr7waaXj-t}N00@FB0KgwcsBnLQot%K0n3%muar;PDSYMN^Ri|#jhQylW^|WY?9Y_FV#$LGQJMxK~_nwFGt7yvHnY-)DjDN&27Q4Icla$#zhCK(k(= zzi6NEm`{fDZDRcz_4bb1Nc{edOKOg&eY)YMwolsZIn8RD=h1^x+vY9W#!K7eUQtu} zRPffkAnu}prq}Oio1gI(P5hDt&2x+H%iBsl>uI}JX(M#d#)bGd(H5$|`2J$rWG4S- z(?(-`d4Vr@XJ;$6;vvp=J%Bw#kn>n$cyp(f@X`K@=odGZR@3$iXtyZu=~U8&H}J-4 zvMnQ}m9%Aqwp1IcT`PNBSYp!<`l&rd$8y>*=w%3}4P#Cl#%RMP-m)C-@zRD~+R#fI zdK)hAnQ?>EcGSt>?gutpGRNjS@!Yw5Z&yE|4fQr{xfKtbYqGfbKPxbp_jjVSXN}XI z5#HSi{hKzm-&JK#*L`!R^BMEX6!s5z{~_1yV_&d;qQ0Hw^s@%!mfCVC?Kl&e<)NJ` zkXbX3Sr;I)w$7e+WXtR;j%3reAJLWzY5(iNKZyN1Zy?Ond$@WF;eGBfc$@d8-a_Bb zq;1z+F~77PIkp0KJ?>ECnBJisa%oL7Z#f?9#~Yse;pNYfN6%csH+R&AY18ravCrY_ zRmie;xl?2u|L;eZ4Mk?%jw~CBEc=2u!cq8m+H@7N%*I2O*?7pZspNM&-p@%>ct3N} zdxZ3+lAhi(-cNc5NN)&lF>N5dBKp%B(kt3kTe^ny;H*ac2CgO^}rhBWbvhu=5*dIZKKU6lgDpqcgEbn%Qxrb4WeBw z(<3kkImvxafuQ;@vNA~i5oG0Z;_Gge3UHF)wWSug!ClvsS`MBind-h-%DxKiezb3^ z{a;9Jf9D-$y|0zw@W6cf@o-1hZk~PR5kEXoLVJHmpXQCqj*pPHe?i_p2`>zX2Nu8s z*MdJRV8+mk0ABLX^HM84{)M9-&AP*C)_US^E2W2gZ^ASn?f1zH#vwdEF211hJ+25%NQCwZ)Mk|4lhoQl_osul%<5 zxTItyZ@DGq{>3FLdCv`*h1~D`PjcVGo=-XBt@Y_Gd(TB~NPbi@){f$BF2>u2k3FX0 zDZd$~@wONU!wWm@@wT@4R{BgW-{^6Ha%4vxykx;AVH2Rd(bC%}Z%pG6zJgWW5evQ(ZBzNZWUWUdHWJoXdsr8IE1K`VX@YxTMO>c3owjaDGo{SQw z6(1fE}v?)F_|U&Ez!%{LWV9d}(ixA~8=FF$fa4>N{+(V5zx z!EHS3E7V-8#^br1OZZl_`P|T5dGo(j+dKvT z!Thh|4d&=yt4qBeZ@`DY)O&`@YJ2W2Pa-ud+7~|@*d?S!x!-O zTlX0y!~391(1w|wZY6qOLgn(5drF$N7cLG`rbK(;;?QnGg9JyG7dk5tX!~3@) zk6m3SoPAD--bYOG{51PbI`ltyKF#L&R>~z^rL>-Z7Bp5`pI(YsQRftOJ6hD<+iToK~HO=oF1l} zZIsJkH=9XXKDDhUi)V%V2PQ(htc-K%H}jo-^YyxyQ;(zim)m#jH_&wk%$}?KVx#sg zW$XKy*X)t)n)WaA=nr4Ovum--WWSel2Wgw;&=+d4Ygh-}YPtW$Gs$Y1i;aWUpkk~o z?_};WNb4hB_GWNd8>{@(=PwCor#GB+68Br4%lt_58{}In^BdNTT4X2D+w`8ks1ln# zmLA%i`46(QWvkwxgdPdj%O)^g+s^v3_FwJuLbe@Q(vE%+>1cknIvtyN)`(v-zUlfN z#(HvicI}bcBF;bZ#!?aGsi6#Wc)NW+`K&#~bgV7*G{%^R$!2Az;(62SbpKaxaCP_Y z9OTMG(pXD;4Ad(p&YpAR6a3R8H;_9zbCy@w_2!fIt3%+SCBK@wAFO>~?FUE5OLC!x zHD|5&>AvDcr+YedmsK14!xLD)ldh}%=$O}ZEUz#vTJMXUZ0xrlL~FCBpZ#uv@J;Zf z##j&2!P)Gp)kU<6yLTK*?j0|}-pI|@4U6?+|JVutr?9^NA}s%(!?G;XcxOK6bg|31 zgMIZOyvf~;U#Cew=;!+{yh}L2<2e``Xgc(cZ>RAm-wem^0RLro@zL$V@jLHSWgPsF z|JcMIY$v?d<14B5WFCBucW(~jzt;G&uq8OS#^Wt{i|`ub&5C*V^~oz) zIIF7mbUV1;PAA2GH3(F@MqJ!8H8g09dr7_0mM_hXM| z`u7Ms$T_I;ekLc@-CuE!-^@4{Id(dGK_UEngZwk-XRnTO{(IrQp?nK}2(G7($qC^a z_MB=8)>cG$V=s#P4zg|zZkor_koNE+6@zfSE1#_J;O|-a(~2;B?_as0qS^%NYH3fy z9$whbo4CLZ_LZ7|TLJp}*%Pt(>-2Q5d)d=Ha5vE1+kJYQY`y<#VFyFNg6y|R0W)}FsG027Q2N`eoSh*9Yjn564@?6CW<(!&(VE$>%J7-%ni}~-n zg716A&0NvlnmL5;tM-_g*@UOsfJClme}O#?yDwfa(C&*`CpIkY*?l#Z)zw#9PqD)j z&_;bx`vj5|wdfX-3o-g{4spBF{sH!$KIR=ly`}pJ?uYP34{&dh?14A^@T;%whn3#z z;+40;Lv!G%wFkCc?*V_!``fOcLiyjBA8MY5n|?*8c@PkuZRMun7lM8hOwOU&>!3Zf zTHo868HSdkVLdckKR-CrgA7cq=N|a*G0IRu8FUBZC;WfW;nynuiw;5T<7_&#j<)Oh zJpM_#46^^f$r(HQf9!1g|2D#*lVQB0Ns{a5@6Yp?R9N$z$E3n(JRXw@?>y4knF{XN zG*5Q8Md#8cu)p+oqjM~O>$f^z^7y1-oXegt)wHnxw%}C!6sKlC_3DZLbo?2UuBKeJ|2v%b z>5l&l{E>64C0B`N(GMsabF&GvkdxmjZwX$^dguh;OyCrNGs8#6Zuh>0Q*@>yIffhF zy{T1QC_@{zi%PSV?T8}iTuXgf6P>xtn7W8;#n1=TcVv6%acnQ6_|*`<2wK_S#4j4- zd=tN@H2EE33=TrWyWu^~Dkf=iU7k&oU*ZQZZVrviXbd?tdYn1tV#!3m~gAMfiW`KM1^HvLNb zi{x`pC^6d3=N}$d!PPoq2=#D>EkJK5}c63SgIh_$L;w~=f zJf21HC;cx>8>nxt*=IWX&<6|Xe|i1kA=?>GqY(TGI>~QJ{xYVH=2gf z1MF|}Zst1tS+|`Yqo12dWmz6}cm27eB!O)&_slGa(NF4a{_MSJ4RZ;Va|d!_?Nrlo z8~t}VeYzF?TZ4YTcB;kwWXKfqWW1dj8MY%IG1yCeGr>J`hkM2H(S!H+>!)_yzpJbv z2gTXO=RBN?-^Ny5cKM<2=fr=Vdo5mnp{$_?v^bUqM~>1Uh}=!mp!66T?B*?$H=#k~4@VYT zoYfL7KSXY}(|>h#vzW5(!S;cT+c6V4s~qlU+2F~L$9cRwYxM5XnRyLC_`Rl=dByYW>gGb@J-Cd!L#R)cA^s0i@)Eu3$%}pX zqqNYOv9Hcj!fGIk4Wc<5I5$Gt?a{9c_|Bh zrjoX8O!V?JhFPZ?U+~qcIAcm|V7P_50^uvuV)k{8-Bw~+j-XRCK`Zg2&1cZgfj8&m3ZfOBIrL_f}~x%9oC@COIzyw}fc87CR`h1YDBeA8V`l56{! zt7;A{xhDB0dtb@EH8V^{Z)99wWZX%PjEhUgu|^b=j6-LNA>$I@-#AU>vYk=>e$D*Y9BIk9plnk zWaD>9CysnvPMh6_JY^xNs+9arvSm$L-NQbApn;PC`JPXDFd;qi|<$F7FQ z7n7f%Y;oMtxC@J(&Hsllufz|HWTSsKI$PNIr$<aU&N8q%PHSH)fa!^3FnCZ+x6$(RmQKoRe#Q)y7gbjxTSk=Ue8y5Xzm*G zX=^Y1o!Td!fmhwTA+*k*vxjTpyAJxs7ezbr^XV5+JsITIi#aji1mCp&QY+3&ZxkJmr+9^|Q{g)#%)h(N5xS+B^^s{!}x=2WN@s>;sIiY;1CLGM#PBXU^LE4C`=#{07N{*S`DRBLm)wx5Ro5XfS_7 zAL$)yN%Sxcc_Y|^XKrvJdGQSV6LI849`a%qv|Hx&);)%fr?%6*xdvTv5PV{2zgqeV z{F0?Uzap5FVOgXt8MY9-gu71`;KTBjx-Iz%CZ)_URI;3=?74TDz#<4Eh?&wFGpSsxUI0OBt z+|iFbl4 z>{zp!wt2|W4>-ePrvp8d&Os*~?(!KY9{L?=C|$jQlSt-Saw|AN^eCmFv^Eg-KN15XBfV|&rVXzevQUuB=iwL4WBTKoN<)Uk~^ z>U}u%3H4#=tajbdZ)`n>KHNziqR9S8ihk4jw`3i4&wyJ$)k%7V>M5ND$O%BX*gieXS*N#yXYTUSG&+U7|9_rC_q9(O|ZDD3WD5{^+!E zh<+xU^wfO=x6R#kdY%1Fu&&@?{XlctP|oniwIv=~x3kyQS_d~mEB1~!!#F2@mbvGY z#%PY|h-8}%rLT7%mW?pb;FkNAjWiw0#sH<1i}5B=L%Wft=IRriHp#Q&^xotR#$Ih_ z&2J^P2D*o-5?Up|Ei`#`*~|^P+9YYOrhO3YWvDLp`P%~QxCCR9?zQaPk4sCF+;wJul{+WdPqk) zk^lR4Tc`Gyv`wF)JK1;bK%?~&J&oeqFW7UN!(3H-C40Z;sn>|;Grn*?2fBLil1C`p+%=|A<&Kbs-h>(^{s3hL= z6Y$WhQOa3Q8lm&X*>q}$PO`l4zl+uo(kslhxq0w@OMk`2b1H2G-BLkxI-=LmmYMLQ_9)v%H?EfK z^CDm9_qFVKt|($37CE++`S09&MH}AWxeHp6PCdHg8umj~Z-3zaKb9Xjk#R>S(|}{0qz8V^G^mht_`I=wtTvUd@!lIYhU0W^xx0DK5SP`+B5*#?U-UUx5K|vNmF{_ZMSP3y!WQL zB~}OTROE)2W(?TMJ=U6U?a1~vsJt=KEi?n_Mlj!sQPxh%r*_boB0aK(aY}SXr?%aL zUUQG^_}sUBqE}DtNQ2H-!DAs{8)H%vZG1lWC&ZV`KjOcGHKoP#j|?*Y2F|D)^s+{H zJ9^b%?t^;Qr0ss^ALVqSp>3;Ad_KYW(tb-rmZt`T6e4ss!v(2H6 ztb6P{%e>OL$Q;`Fk4by=F30m9F5P=4wD^c~y6nf=w$h2T?PvXDqG@D}gF6w@4z_a6 z@Fy#YcF6unNlX0-tWHf~Q84XxlR;gU`u<2M$ydlPv4dG3UbC{pZ z<1VYg?8yz`eGEH{cRB7v@1K{6Kf1#Z-siCWd7tAq+y^m_`{xF;e>sGAI&6R5>F6eZ z?xGva9>kEKq$0Jd~9fVIL+>C~gr+j$bSp|gOK={OH_;r*I z4xcrc@EZxgAsSvv`HbhRp@e%0zcCtq3*}4raKh&h?u~}epnPcAStAKwMEIO&_k^&7aVpN2%pEEp@YBHh_iAPbdr2EKM``Rmq(+&GOwr5k znu}J}_T%VWVk4?l?s@&P&p+0`2RtTpgU7kRph1~7>Jq$H4?GIad9YBGE~!(KFXe3h z2EGAHXSZKhJ-ht_$_&t+I`rtq4^XaEap-5-Y{{#XkzB!Du!@aM!s^fgkTZHOE^@yha-CrQ7W**g7W=|=xOKQXuEKp} z*OJtqN0y|n+qJ|~7g^$|+LAVG5MRZXw1rN-tND-PbMvnxj60}R_rBz*sC&_Ks&1R7 zkx%ZW?yY;t<0h=?-sg#Xp19{dPQJb95^NED0zT0zsIdKD@+m$iUse1MChx&jd~Uuv zzFFJ>p2fY^W880*JNB<~zjZeETQ}q9&abBySHqo+E`pahhs3caz4)cC-jI2nT3LTx z1-g$P8mWcND%JN8dSDe=N?^R!Gq6{smYC?*%%3~o)eQf!0Da8WaVg1LH9p2}>s@-r z^>c^07O+mACTw-0N?%RbYUa+5-pzc%o0-1_tnIV&J;<6~z&bu7AL~1U4Zit=H$x9g zx##3#%`ae`pOKICuh2o6Pk1wTa;4mJ@_`c;fCtXV2YxWoMVU`{Gk15T+;j4QGZug+ z&d66l|Im?{Pk1wTe5KrT@_|zpfJe^A2Y!ig4|YD`&B$}4+;j4Qa~6PS&d3M;L2_I= zpYUex3`@D^O3;wwmoH`3U`X*tg z;TrrJ5xh=#V~Uz0`S;r5(e2@5Jy@_0-mMs8j-I9BllOwltg^tv9~yCm_lSNdcX%en z#RX=7`%X|-1v>BC#|Vy(Q{Ul038xLk8t+1ZQ)|H6Ll1YB+M{?|Me%kKd&z0MO?!|Z z^nC{RBk-Qwx!DhnJ82|0t=3WlyTR$rDy8j}mNHj`&EW14ANABx&lYg-E3DBo@b72b zMPjcO8)MGi|5z`yMps)nmu1X*fK!v%13ZN9Pzh z7fzc=JL*W&NI6!1(#gC`U>=gOk0brx zDmack!VR4P-Q@ca+$pr$MYK!gi6?#OP(Akw@D6VbLKKy0|~( zRq2_5a>g*mFUq+<4fc~oM!bAe@#s6>C?5S0FjvKyg`5xk4f?qS8Ew_w=Gt~>FVVdS zL02Pt2xcQ29m#pj5qQ^9BQsEDWVYq8xBnSlv$n74k~321?xvfKjKE;#v|i6{lYPCD z^Z6d6le2ed*}?&ou^Cy0oWr(I-`Z5Z<%CGz9JxYs8)BgfqkYO=PvqL z${)fQi~A(?h(1U6v4DSE^9AJ#pDgx@?_?bO@bNMh8ujtk@)na zIiPQ!LN;N!ggc;fTl`&gZZkBLQ^f_8jSsvi?J8iMm66Y;)My)UE_7}IYr}xfwXr8! zaqNRa=N7P!8PK^l_B<<`eNO1y0`@5b__ML6S>fzsLgyB+4;h0AXV0?23%cms0`?gL zI@iXYWQDU237uQOK4L)U+SqffaP}FYa|_rf4Cq`Ndx{m#J|c8(0sDY4k#P15D|~zx zom&8&H=uKE>kW5I@boyx5B~mLgyBMrw!;_8#vtx2agM#TL2z5<`53f zw!&w0(YXcSSpz!P22QrZ!NWr5I>Eyl_*MhI3Ooqx3f#(jo!G-}DgbVHg z3&51XZ3=HWi@ZqA9sH~np@DY-|J`}P7!L?N8f5%jR=v!b*o2%BrH79J&wt7g`A@h@ zx7l%)j1o8&dRXKE0^_o_W!-z>0WEpJWm=aUU`>3m9D5le2e9M9TImdIhhpvxz z7Pj0Laz4VDpclIXmM+*I>$5|IdEq6O!k^}{KDIKaUm{od8{b!akHM$5?7Ael0Uq{I z-tZHCb=$8;1P>p*A{ZLc^@gBT-dc^?HH_2zumJLfxZF`xN^ zI0wLHd^v5%OGd`9LiL}CeZAT6uqtI|wjX|a;0QB33)#LKxdZFWcdT!Hb`f!#Ib#t1 zqnvq`I;9@9=(~DjbxM6wuhjXk@XHSD@`Y&ECdw~br}d}oQLQPFY{z4&zvBOg^hq~> zkBijk>Bv;dH`Io`$VPsQ9OZ|=!%^l%WG*5P5m`tJauCx{9Wh%JyREn5&$NLddLp|i zyAzv%Mc}{CLSzZ_&2f<>*IE1&a1`QfRL0XmnSS)uTgg)noaI~ZbDUY;{lxu?lR+Y zJG7wuYrX~SXU6r&UgLO&xS=bo1pH!alz*G=L*mg(FDZYg zyuB8i*2gKwV9$3j*T-p-+`A}dP1dYaiz|4qA#nm{#}{Lp?^5o{jy1!!QQW61GQ+&! z5`G_;v|nq6-%D2ET-NH2VrgU1Q?%8(=b=qNhVstz_SSDYwY zqaLYKaElXomU$95-uZIpdV#w%%7RW>le>cR_!eyXDAt1bnJ>4a$7$QXGIwqVwtkUG zO1%4qo~P~ny3_dQjAR$>b-#9Xhdy3)OvkP>@3VL4YZ%WE-_Q_Y=+k@W-6Ptklf#)*idEw}C6QIrG|`-xxt{ zPGP$@+w4FlGsWF*qz_n{J1v2@CofRb;>qt$*^|xQKDC+ka@>e7e2_I<4sGnXvF|ix z(@NAgM!G{&$vWvc2P{q$Q!x<;Xo-!f;Q3*G_Qm+iBiz|PN|YUFWrb5y=6 zZDr4wKDB&PJi3*=qea<9x8xDdoz{BRTZ_#$+N3|NZ{vsWZL!-%dy#pxawoPPezqmW zHrhV`|8w}^Q(Mw(qeBJwL-^r8TQY5i zHrnCD@5K*)*HS?J4*WOZhi_{cO#Qj|P5kgK-qcclF)7>@96VFY}GoxhircEAaZb+gifFPYkbsiqcu-me|F!LM0zjHe>n z0gd>H5iJ;@oX3@Zz-jo$-lX)P!rgA_2U>Oi*dLpE(8RyluOHZn|3`lOxWA5&9(PKF z^tjI{!>aFEWds%FK4}^Uc2e%urV(_IztL|T&~SO1E!aZ(`iQZ(ocMx>K@tA3M#M-j z!gcUz;!=jRPueE^H2Ff%n%Fojc@MbI|2-jn+$+Z7_*bx9{@MW73C8NgYk4j|eEUfw zzAH_HG_M(p<6e_A16?OcbCNWi*-%bgSDIHy^CsoId1g5#eEjKhUL(!hl=JqP<#68m zT{&-(=J%BI`!mZC`t@`0ZtNR|whcQ9~~|$@s(_AmKY~Lv`&kmN=}fb++j->IG#ROrJs1s z;gWUM@|x4-WlxcEB#pO#G{AN__S0m1kCNsnX-)PT?mcy-8B7}BeL6PPPLRK=E{99@ zozvwEB@Jt!96M{>%Yg>?uAJedL9Q?z+iTs+X#t;0IW4rOVx%p-g?4ncHQ(h2zj5z2 zeKq$`Cpm=%O7<-vz2L2V=yG>l(P!+pfUDxtj9r znKKjnD{;x!fuB#QlmLAFwDG-_t4vF7b0XJ3erfUV*MX}eM%m$5TT*Fu{d(vo6I_Z+AQ(G;*$wHz(2$BU9MQ8eX@n>pqL0`z-QinA~MqoTS!y zNiV#kz|$FPvD-Pj^u^#k(eC{@Us?sM=d#z!olM%erh{@++HmVmoaoj_K9Q-!*7@o5 z$g8DK>bdkF{j&N~&v-ZVR-!lE;fIcuzTM8*x%90EJjzSZNiVy#prsp%4exs1o%t`_ z3@@O+p&u{?yvZA)9reh8BErA8yTZ`-DXmXZi=ll=Lsxf&wQw(iGwa2Ea8qbfG%vF7 z3ivB0FsJ*^w9PEMi4)nCv3o$!2|X2uiQE~$Aa_NA6{ZT7AtR}C*h=cSGh8=@dv`Wh zdL8`Sc$Kq#Mp~Kcd*iT6KErXII6ra2&l49R&OA??x?uZ^8_pAF66ZZnoS(Qw=ZT9D z=RZ%Jx^Vl9HRp*li3^=4&QIKP=ZT9D7dcOy%H2NW?eoN$#I>9!&QIK@=ZTBtZg2Rk z%Lb$96t~=~+IgeMCwFVwu(epkKF|WMuguFVxE_1jKQcH){^3_X=S{t-{LA{1^_bgj zJzjjWvvhrQJ;vZm@*I_QiLIdu@SzvpK;T{K-a*~$m8D5i9yn1A=B~stZ@c5`&TTgm zW)k)_t^>CNSK%Tjfrs1vl;B>Ahek$w8plNCHG+TovR(xjXJGqc0kqH&@b4`6i9geC z(dlWqJEwnW9QcR*2fR@z@)gndK^9eF$#TE0IfNYZVCYU2-hgaFEx)~8Pr<`kF?X2cdrk5pUx=rP2aZ;0Ht4_-Po29*)WhtYkm9RPP@+rL;gT`0rPDnqlsM zJF-219iseuNO z&PG1GlDO|fkL(2hOqr0qQTkUgMrEY2PvoN)+^MJeK$O8ZvBBSfyhP?5TpJX-napFG zA6(taeX29?=V|?nE}d~1hc=^cp^U*K#_bZm)BVTxsr31{HNU?(J-^-MaKe|$T=!*u z`!T=$ncoHMA63ljGH{m6pXhu1)#zU+`&79kKgwP# zGP05t%htAnYlT)gPWxoM?=ScTcHqy@4X-l(GhcTE2F#tgU9s;9 zZrsbB4Ud!zJ<)5z>oZ+jpc`gDH`G8|)JACsr$YtD87aOG8DBT~4%0q2>CusCJIa2b zS^IC)CPeIBsioVp*ODcOzLtfrGrnJDDSWs!R_wE~#{5@m9mughUCBH;{Vw)xW%9l8(Ubx(31DvZv0GclclYbx;=n%% zKVv0+ulSiGIdit+sVh7cKfJsZFMjU1$QiK}Pn*IwG{J@WbO%*O-*sD_!1{)ZDq943A~&lT|Q@CuR{Zz8iO8gPn*Yqjs|?h z)F+Y>0vvIX1y%<;U`H?t?*<=9xJ^=;cq zIP~eLmB5pv6C5-R_!GKIU{QGEW4vWr3@l2T*gf*PC@r&!^pZ|=)kOEs51iH#CUn)s z;EDGMht59uJ7}p+_>sXqewV!!e)3azh1OfMrls7iWeih2OOo-YgNq-+?hrWoApMzI zh#!~??$9!Pr?6vC*W>N%B>H`lb6eq6gum&*cvWcefmY5;_3D}JQ{?Y@j9AYdx_!ksfeDn@f8)34UNt7I`qVJ@(=3h4Zheu-$$AyO8+}6bZQ~1t@0K^FgdRy^Ke{H$4_^xmi4LIPtRZoU zfdcgV?nQrZ5=||No-3i@8dn(8{LpLe9QUDm72oDY7oYcsrk%KPX1h7w5!f`x zTP^F_Jw{D)BO~23$5E{TJE9-P{^E=A!!dh3D?7XP2ODE=oiEU~Rp4wH?}Sf#2E}g7 z>_TvV)OL)57s&m_2n%l(f>+36yvDKKbY%$-zGPJb4}JXXBH(# z-^tr9V^Rz*oWS@Wf2Ux(m-T&&_8mutC;X}#yf_nlC~?AHafVPb9h}(8eRG|0iTgAy zX!|r(&K3$cjaSwk^pm8?=WhJHujFnY7vVevlhND_P1e8<#;3i||IkY+n4@Ecv9AK# zKC-s;f47BeXiNMUL*8C1)g$jf`5U-|0Ut{vxsyN z3FqI*zvy`6{x|HKr}RkeX-`Z_w#CIKC`~u)>Yv*?j~Hc#6>CfODGj_P{@DGd7@aCK zS#Q#)MP~Q~Xv-GXzDauj66if-9#pYLEgBjOA>-ixZRJl4hLRNY6|y+aQ*P63K`(L* zO|3jQCLX(EdZx9H>>w_Nr%vDLV&Bxn=EvKY2Uq@*`*4(re~r@|1J90r3gy1VURS1U zi&q23vR6ld^$;?N$Tyv%&MvR3tSf_}Lu%n<$3Lg}%#!t2~ z&JB60L-M46e=o7Imnk!Rv0e6sR9|6=86KQwh6iS1mznjFz+SF%RxG+lC!i$*Z#e=_ zT#&c%6!s){Xi3&RH*dadQa&?G~O&=UoQk#vxld%h=R>poG*#-kvh>Q$sC}d2?-PVDRzBn$0dP( zR=Th8kNj(^wElp!A8t+=R_Bee_ zXWx8?v1mpf`#!cv(V-~}03+DX?2s|wjz?g+N)O0bR8%jj2LB0fII68%TQ)NmLc2F3 z50|lEjMInE_fF>FE&47vrV&|^;Fx{jOu1iF0A3LPM~1Di2QVsanqajl$!e3dr>$Fi zD*j1(D$>;`p_gQCWNeP5sZs2~v?uX&d%ET#!S_a2d#2DH#-@kw4R|x@XA?O0S?WJV zIRYC}KMZr}BJRAskA9wvi;Pts?I=SR8QZdf35<)6ad9&)yOPx?8Bb}$CS@*lb zUM2Y!bQ_mut*ag2dfJHn0oouqFjnVt|D?|1?sZ}t>1ec0c+#cg@e52=SYxgGcsu#@ zT*T@GE~o5(hLAelT91<7X{w|cJB}yNyRNY7$*(GV$#MMBmJ0Mk8dKu>*JaxJ7i4iS z1A5wEp3+!Hp2)?4%FNWjb3KM{T+MjxueEfwgK3P>e)N+$??ngDe^7Y7Arl6n3*N(* z3|##box>BzOHLu5Xr>*&Wm6k4QGm{+!Q2T=<+iQdkgIGK-V?alfi9TH{u=)u178K~Ndo^uYkna96l~h@FYSw! z)499Pfr1r!M$rl*V><3`?zR1FPXB3QlWQREzI2rylRdo&j@!Q&-8*!9_EsYg;Lh_R zEj8Hy?+4K{ZSZlI1-?Rij5}Dko6!ApQ$`&$+4a0dQ~|!aW|2xiTZXkI!La1Po3SCV z37ex+S7;eMirDLKFgpe?Z_J(CgXTQtxn55hdT=u3eF}dw3A*ny?t)E(|G5D88=;a* zoalvDEz6xIvY)EEGy1!^lTa2u?9yEeNmep-0Og;X54vK@J2iJ zJ86g1FZD`Wr0!T-QY0Pd(54TBIIj}j0J&??=46ei8McM3;MhFP9(2G{ z!-KW$B>dx%?AMHaU&{Io z;Tp(?ZO4+I;?9D{m%i;`ES}I3N}7<}4TKMWJS(pMVBq37FroF(O6un4Oq(>f|Flu2 zo_;DTq5nO~mVU3&(@Ql+`iMw;`ekNv`lXSc>BG&;^ot@{>4VJ+(lz$HaVo(&hlKYI z7bE}c!(Ldc5|YPpA8{Ui)`0(F8{zDY%=vfW;NW8P8T+tTq67I|IP0Sr{l-4*pACAq za#(A{Y3Kp6r(UjX$>X#>R=s^#Z^h_6CT5x8!#d^1!oeBE=szarNI2!k!gB~mAF_`_ z>ditn%R@i_xF#GhFV?Yu}YW zobchKA9+^!@T$e=RVKR5DjyoJ82!q`F=v%OhH&&P`de9C3SbM6o z6gz^!r;x)pYkhqw$B}?@*rfbZr zJkW!ECxtS(u!#PWEtsg{(3gu3Hms;wYs{P8z8u}O8OALxw?hRNvX|YSaf|CSO$*G- zzr}Uih+ABDC>5Ape2Z%@vNWLsj?Z;gFRZ%7C1;gF8;*)sO9x=zK+a@FV%K1a>Jg}# zc8lCINS{%0i%aOqSLZpaCy;;fs#{#hw$p!?qLxZo3pd~5x*a+7A)8uyDdDf!)lx}6 zd+jZ*Eu^3Ig4CZJm_mLjZ^>@jf%`1!ZbKHe@QYhqavr?+5alADc!F{y|KpS|<;^^H zi|bX&`8E0P#SIcKZGMP&?g0e27qE0D;R}sh(dpOd`>n1rzxqtd_#Jtqk6)5!N&c;_ z#UpNYEiAs(s!z)PoV=e=j@90(TU|@0k*4BStKSciSK763)vYc``zq-UMEl03w{-UA zTV1nWApP2|^b1KZ{aU!YEBzCsl|D$>&rAA4q(646Yvvcx^mDq>Zzj)AiNBZht4Lpq z%nQEgxrT(|4#i%s!0+P}x_!uJV?5HCy7POVgkG@D%5%Y87eUwLvEOB|-?+i?veynk zccM(!eQiP?vS-PDHJq?YSjfp1~$ zbQpS*oRe`DeJ~Q=Cm?5IA!w2iXJJo^D9P=zXjj^$jv3h*H3OClPjt!h4_F|7uVUK@{b9ZbvHkH#R(YMWG ztls8M(H88Zi7uj%iu|6q_*9i1`Z43g9by@?&x{^TVl!$tJZ9(`czp6TP7^s?T$9jv zp<=$Da36$rH2j!zDE0>z{|@v7Zp*~r7|SZ{I`IgkH;=Ti=75Ny8-&Vt5% z7~6e;2GWMgM6Q4y5PM5#y&3jqs*!Q{<@}@tTmCPUnH}Fi3(I{Ck&{B(AhSn~#Mgt* z#s`f#^;75>c-E#t%&nqt4fV7SJ#FkVE>`d-S%y6@41J#a<&1Hj*Y&K{v*do{MaR(F z+z;JzG43mTEt-+M6P=YjY%BN0wxhfg@k7-+`A*Ik?*IqN8O813K_~PC@SeQhP|1hc z8@^O}@;64$LS*fQr;J4SDkHgoG}n`+N{j&aF1T>Kqlqp52obs^eF) zRY#K#TVD%R$8%MxugfsaxnNoV6KEW4O)q=Huh?>pkN;YIs2X|#PG zIyJ0;gL<0S6foN4e9{JOTMu4wzU|*2ZpGUxH>i;4l4Z8-gg13&suvwLQ*?d8juc$# zb7bODpU9=aiD8Ewm$o?4aB0gB(&ZC2n0$GpacD+~&?ke)7q96hb#|4!7oI@UH^aZ2 zP^v`Ujd%;4(q`m3ze4{Mxn(lzkx$D2zgRXCq%Ii?&cM@0(^vVIbMSTabrk+Gqqnb? zGyMzc<3ZgF|5vCL>`Hjw#RvcV5qE#~YkdQqTAEMJ z(4JEIvE?9zRr*C6!7I&>JMZ(@J3^_!x#b(G~5e95P? zHV!i`bt$iIa06@O=lsAWVe$A);4mJ)!mro|1`(Ek|6Ozo%8UzrL(zTE4fM%U(ATUN z8ADc+6Ii-g_>aNZiy+QT87A=QAl!SULO1R^y)n@d?XqRjb>1(sx@2er;NZJCO^MDa za1&siw}6{Y&v#}t4p^!qu8ecC`Oq@5wgiqpm?~ zdm4^K7hHqyy`HqPCaQ5ozAb$^`%MU0rxlEs$h%|fPxKFv?Y=GNYiiAyU_=K8;8!<{ z3Cj6W1bZmWd3=S0v!-f+jbrE-ip&@IKW#^86=!!6X4UKPAA~MCD0N9aLZd{ot$Pre ze?Y!|Mq0Jz9E7-Q`SWGm*SZj!$ELTuo?94gROLd4m{11i93G&bw?u^OmB@X=4 zVPbP`RJEHP=&OxlPqpc!8 zKfAqR-%02KW#gI3l${$QM%iI#mjk9!0UdvlU)xChJ_#3k*8y&mf3aaAI8N}g%yXYN zodK~G^d)qI&`LjkGe59^@mdF6Aaf(MSC$8w>ERO%jdO5o&0-j=HA@<2) zccH}=n0v+2LvsHJdpJ!q{?nGtK_CIkJHI264M4zhk-utjO)|Rnm?S>V4pMcnMZG|4gFv~int8G>PC^}TL zSA8~^@hYcH5-;gS)6Oe_LkWw)VJz=P@|yqXe6iIdcb9r|4yT&NpnLX>M|I>XJ{xv( z(fMJGzQ~@y*$_Cv5PYpNu^k0HOMku4=;$)lsowj_Nzd2?W{~IfoSmVU)OBZAXT0+1 zlZ+#Bmhf)+Y2iDT*Y9n3*Nfy1%Aa@ zA2O{D8E=g`b}^4vP_Mwd_+#Vp$Ng6Xj$#M6^9RsXoHdn_NA7qPQ-|;u(?~0C&aGpf zj?(`rj9-MYll#`ZGcLBZWL@3|9pH_wOTo!itV^N4hOjPovVLSf zh4va1ol^;m&8dW!GhfGTYMtygCa^5<{^kBr0YBkR3w|>Kt;`+zEFHDF>R3yg(TVPO z5jYT>6Pt?|?h%-oPJi>LZx^t8HXQwdv642dB#qSHJze*`@knc*VQQHn+qLROMSaJ7q{>^?aK$YW&Q``iq2h} zWh2djJ=75Uzmzou8qhHenvr*b6Psp|){V|&0p;+n8~L@iI$T9wNn;~k%D5Q1&j~&|29B$v z9rO_xg9fNxX6f@UH*A3reJ`MGPWp2*e(BFRmEGo0+56^0*X_^^pWI*X#1*`7%;;PA zPEY#J{b$Buq=R;iWK1fcnb`+QYPc7$j&aecoBQE~$f%}l){T-3c=9T6LT~oos?YB_ zdlq=~4zEn!zz}FXvjR?1OC!p*8 zPZwQxg*E1V3t4OEHABaI*11jSHR+S+HdOz;bELF;R6cW#?Nq5p?1ac%$~jhSK9}De zztPJ(^?8hoj5ji*eKOW%%&nWfo41Phl`-aS#=DI19v>a=x4O+)8gte)mU^2L9!B6x z%DB3CKtSFCK__OPlq0;M#E+w#f>=4wO0jvHakp*bfc)>DH))@={boZC~H4Hpzniyp3Jf0B~I2yUbnR&;{?vK;=gb02uw5)FZD`YQs-6p zWAE(9nw2{%|C2R4gf&}!dd;4BUtIe5eQWl8_$XPkEy#Cd&3dEjRp3;{MDp0ryIwQ@ zKeApkqwAGE|8K6>xzY9dK5!&+vpu?A8Hev(ud-IZ04C9EnUb%JlG~vxt+!)Yr`yh0 ztI(XR)kVk;YxLg#SJvug;6&DHh_!kz|1v*vxBt;@Yt`FrtzN|(%32k;m$agfzPzS! zqxnBruUr2wtyg&A)9bbF^m;va-p;pP1&(8|DeFh>X9&!9t<`g3z9bqi^uqbp>iO18 zY^_RtQkUFA5gs5e$^!_^KLJ|$YxL@5e|^dR=M6$%STsk}ZpT;bC&)Ru3t$vxKzAHY zRxd*5A@}TuEJ$aM>Rq^D(<9k#aQ(7PD*O`iB$2tFKn5rFb-fbKl}0ov*X5^mp7s>-8Ps#M=%J{K8B4d)6<^$*mtPt{|C(~j=2$a`b7&za{JU39#(N$4PHOLrQlds|;+ z9tM!EmUMDoM$$GwH|>OOYJf(OxJFqg!at2LJ7hl-`f3mOTyXV(C>`_5v*uIgP3E%{ z9#Ckcm+imbAaf>j8`cYGmkeik4IUbH?7FrIF7lzk9E=P|<_&n3aVw9`TRC$gaWR=n18^s}P3CQPblzh0 zr{KYuO;ed0;lr#o2c5t3YWS5j^`hLFlCvTM+QNz4upGWb<{o-yTOKmb0_a?kb4pw; zdYS8P*tGG7rJFal!fWk>uc?KnNsZ>MCGQCGz7WluPhM;+S$Qi~Y})8vv1Q}B3yr|G z3-thUoo(EcY}?9Nz;4!$&}|}v_zJi=qN#P<1#O#0TxQDeH%Pj5d+Rp{|1}C&79K2{ zIxYSRo)6n}5Szw%e8_<;{_fk(ZHMUN|Fq5?>;-4nc?o>lzpC@}y6?t+W}3e^Sjw8) z!JJATgx9QFk=cI+eUiRRXMEQ%ZnwijRv`Bknc?};xm%VVx~E&(cStL98H0sZ_RVGs zcEYFm`tG<|@VTt_ubC@>t4-)qeZ`myZ`8p)`d9Xl5c|IlEc^lfumRj7GMgU)yAtN+ z6aMHn*0SIoksSy>IUTqb`A96S_+x1$oyd274u1U&JpKmcJ8o>Xm))X&_B#C7Wx#`u ztU&ONguMq|Fv;%)E|Bl6iOF`f!e1PeJ7=;l+`=b(PR^RgHaxOh2d|4l3K!Cfm{33ux<$ z@WdS&cf<^2J8|?u*1E>n=7WozUOlLRf81XEfToOqC-@`Z9wuSzoo-ToU=Dz?qRLP(JX4+(~3#Gw=&< z7GaIHaA$E-K4&nWI)j2^#jk7oxVcjWw{)t}8u7Wv z+#dvog*vhSP@UaNp-Y%MQd=CN?=6ID;9T_U_c=sv3JvR}PnM0mLFQ>2c@4rdoqiW( zk9AgicD|2Umomnbv#x=CZx^(j_+`zDEKI^;xQIQmZxU@298sP-=&%WgzI<%x_@V{Y^KVzfleZiJjmNm}{TecY)YhmHZRD(Z6ISNr54jud4=>ZUCN9A-z6%^!AC zuM@kG*lsFxbN6%Svhwzj;+>RbBioNseyyB~!cQYF@;!}?&7ZQ(@Xia7C2<$@4P+4S zu>apwbJyCIc}3yhuT;Dr`&=)%51PjrOB?xXxcj((dr3*ONAyctQj-EQ7NSELKhg}3 zBi>#U-~S`@>O-`*nSR*sT6#!T&uhP)ux7^BPg%{}Nom2JbTj9Ja!#27?aY2v`b+k) zS_fx^+`rwFiGHl&Eo$nkEg+0LxSO&hEG5_ieOEh}Fz(@Q!Y-_)QiG<14JC}bxSP=X z(6DC_qR!gkgcYd84F!rjPrZUBVU2@Tx}SGsP9X=x=DknsZTPW){>uIVfoARkZ{;jn zq4VZP-&|8R^sjaF2;+mb?3=4HCCnaNnWH)ypxdR46X@ehIUh2nlPrDpB;Qc(B_91d zYZw|!Xw=x5us2JMl<~!mVtOujmnWjXE^{E~_RY{_G8d!aEwdQki~dSnbgrG8jqmIQ zEd(7Xw(B;H1CNtNXjqZ|p>t4rLFc#YPcZjyuzuy;S~{pl^&oFbS6-dG$dF4{_Tb$h z$;-XHkeuB|^TF4au1t^S>rFo9QQ{<@gE?N=JDRT_`IygGKIoa!mHndmE+8NC8p{X% zFI{;-G@p}v%x^3oIJdOc8O=99EqKLN+K0V%zHu^w&uY0$%bTs|=6a3RVz`Ve+z;q0jck=e@X91!l4C1dlph$U4BUf=>mPRue8~KB7Y`>-Tl=({IT8 z0zBl)tmD5W9m+oZZbtuRY=?ODJ|%r3k7SFEMi16g>)lHa-H7gG1lcC*KS=w64aKTs z=iN)!D)#vXaDEQ$)4o84aTWLlc|yfk(e{KfK8x_txMJJ&yT4dGWwnR55xooXlg%eu{8E&R+* z#x8}gAe;wE3xx8*$fM=65Q0lI8%miHTbN7w#t~E zJ%?R-FK;|K7QNLM7_(5G8P2vJ%D#D3Mt>Qjn={sCKLcEZh&zl;Ymvk4VLTt89kUp- z2;+zDZ&36qS1@LW8MAf!uMDgQ2R6_z!L^Ny*`q7V+CTWJbA2OyF^nAR-s>K0deksB z)73lG7Eb1z{XyWid*AEm$5(nzKy-2{I8(1hheX;UxOpA2X3jNyPt%qNZTxn5*V~i= zhy9O>er4CWwe00;PElCi6t6;-TF$wMz~Q>$uD8Txe{XmeS^?VtA>N8+Kb|rPyA=)3 z!kZyScA^W_0DP9O0ycoxhUJV?K+-GUZuZRI0vGMb_|!FGGdb6HJ$kCZ^i(ZBj(xvh zp}^CQ8WrvUCM55s0Q)kuN(1mP3i{&yeBkwR?s!}Y%(^9xJENo-u_B}YTfEK7{+J&6 z6=zb(xac?WPCFV5W2EvU&hJEE)1>ZolMq&*>ACa^8MAst37!;K-UaBAW1eEV$ z?x{Ea&Fs*AcU8chAo7{sP0(QI-{urP1N^tb^X~f7^!5`*20EjC3nRdP(G_y0!hIy> z$*KC5OgH)_>%2Q}lFzwf>Do=xWKDIhkEJW*e16k3bSr;lTJJq`_T`hb(^X`tuGG?t z+2g&2Em{4}f&PpM|9~vK9y*Ztamw&XnjRgJ2K}w>X*Qmj<|F8VbJDCoGtFD*mYtJk z?U`w|vcAqq^U#@T9)xa~PJ5)^#k9HQLiS7AEn`|iyNhY_f-j^TWwqHL&CxveGAoU4 zr7=izJ@z7|8~S(U3_Q1-i_txcl_TRLb1db^Jj)o#_{iJ~49J|z+|S3pLMD8{PzbT6hH5bvtCOX}(Rs@`vmN?ZA`%wXAPo8hjL^r9#PSWC++5IA@Ke$JR+6 z_d$LOJbpuZS%U&sHGNfA8rB{J6KnW{Y>_Qk`w6Q0E;3&mv)qA@Fo^=We02P z`|()Ud!|KK_`^EzDKO&%9|?Tz`&$=$$=bU3y)O6?S(VB76qC-zxWMZ!HECyYCi4P4 z919C!TdNqEsp!td!jy#N5C&};3-i+6V)iBYj?-b0OSg_(r8M*{P-*QC9v;5`{v!BsLQ<^ACs8UJ#|w)+`)7oP7L zpWj?3PTNU6-;kjy{XNBkbh&?5bah@)6ROkU2j~bD!LdVEBIG`z=;BVnA zSCOtee;LzJ5k9Z{N@z3eWq)5f1^=%?1?RktD14o?LEi#Cf!1-u&q@8Uwn#hV{(+QT z6n$$^dE?G0N8+_uyg&P#c;U^YOrgoYuYDh!C;UTeui-trtnar6#ap3;LfbeQ7okhM z&?usd<39-vDSh^iwsbP5@xGtm!@i2Gl(m<1z1<-Dhwzu>N&avZ`}sb`DbdN`}7L-I*}J%o;@4OuH~Zbmi}>7}sm3Ed0L{dynO@fvcO1o*Ca_^nrw$?VNj z&^$>&8$95ifvV#bWG)80pAN6L`x4c$>oVcZdIfi0ex@#4tZv3b#q6i(s?O3gv{AnW zlOGCfb+si8yBwbyY5PR3WQ$izBD|T@+WF8_p2e7|rG=K{D#0PPaJ|5dvh$z9mloDk zCTs4Tz{F==SUJ>=D+0?>=jQ8c@$6m{REP3T_U+8Uwa1H~8q+Ebu< z1#FLP_wWW`Anvi3J^9?XbCwR9Y*V&?vF;^L9(U*T$F_O6BNCXQ`^R#(gY++upSx<< zHj#Wz-s&pg&Rhk4E8k;JP)>5djbHNVk3CK~NvHGI65a^zm-OSX*C6SfCUiP)f(-@- ze1)z5G49-_5OEQ7;&s4NC%pQ#z!&!R)2{}$ zCIDL{z}9$Rs~Fg75#Ic+yVsVao9R-o@8ZkSFB@Q{C%-dvdphphc5})zndX#>bIkPB zS>_a<&73kdyI=aH|2Z-}ZU4|I`$uI=+4}9c^ws|{bc%06#+0elHAU)04sZkYoq|X6 zQ0Mj3c^!3@P^SrwoA?bS`?Kh=y&cG?oZ^ot9Rri}xeBZz+?^EA<)OV7)unQ1wrJm!|bBubr5 zL{9vvJuU#h9sKOZ1=~NWp1J*Fy9%7B_HK9RJ(87*3Z;#7>vFmiClHtpjQark^jdV>2QluCGw#TX0`YSlE@)%yvMApi z_dgHhw zXEN41pd;Ljx4hxyBz!OJP<(E_9@s$Kt>m31P3-tQ{Js(2RFR{$6)VLXrM&Y(SQ~o7 zyZftcn;GYx;M@7^LHluE0e9!88Q|{}bQ5iDf4ibSo4p?!B1YSFxS!&e@Go(536pmx zcd&o{b-jeAw0(iUjPMU}n^n)Y&vD<@(+aT_)V5i-6;7cJ$z$k7QnN~Llk(>4DTQUk zuOjSY{M4N^p0p`?YGH-4CDo{udFJKVRU*CQEu-9HxT|>Qa@8m`ax*p^zq}@4GVjDq zJC=|z`LD0?&zk;XcEaRJ?wo% zQP#cDveSMvd~!MY-8s*DestkLj}=ZHmGhkE9@02-Uh=$Z8@_N0Wjs#Zjr^aY&KWrc zp1q~6$&Gx@oF_bWrNbw?a~}67(m82E9=63yzH&YTxIM-_&K)Y%H;?(O;LWZIm9+0o z_SA?&}CaDS~*t-Z8hPQ~^z^l=8op*NCfh9AmNy}ryz>$Pr0*0gPU&%!lhCLUV6 z!anVboUCb|5cXnTW?>a`d3%n%*P<0^(@y1N^$Jc;FI?#ES$N>aOSV6uCluD*a>Jod z%F_yuD_dbt!YXporpY`N^AAs2TF`^D5A0|UN~g}=X832+`2}@8O!-yJ_e0dVh&n%` z&W{OOlGm&7Qrc2QoeQIN-Y`9_@Dg`w;hH!Vo*k`oe|b-<&Pu{g*U7&hJel7QIpYOp zc!<*s&!)a5lpCbZO6t0l{KM|DPg_HMKPCK9>Pw>?mtA1*b;(_6)9UEsu<1Pu6Wj@f z4<&MU2)L}EkEgt;h0?FtgbleMZQ5LwSa>b}wm7wfv(3`Yz}?K0xMXK!K%jy(wwJMI zcZ3cy0(REG9zN{yv|Wq)8L%w+;PT!uwv^iB9InlR^SHLRNq>TWd0XU@_4V1aZ3eo& ziEV>aLO|9^8D+eW+pH3-HCL!76^>JBZ8NpFLV4e9f)`p2=RWzB7tw`Dm`S!Jpz_dq*w2YA{J z-gl7oHk&(??=#+Q;2n9}kpHIlz%xRNdW)K`dxo_nv|bIez6fjY8P>;6VE-A`K}1~Q zcj3B$WhbBFn+V=KW+W7LsuW*r3}-S1GLG_|T%AfNX+cL{+L90c-K}`*SKg2lmp9|U zd81GCP$TnsC$1hIbGMCfc^gh#-hwN}ZQ&d)pZDK{57=!dT;6;W*FijP3+HF~yz5qv zEMPbGTJq&BH*tBptr)ijx-_5n+Uk)D?8a_OzP!;UE^o3GhKbyaX_>sg9Cwv|8;yy||E_-ol{$s=gC%Z=y9wJ`cEyUxtAcxF< zlDTsc_B3(0j_1tLChk^w=M%lTkcON@VV8iu#CY7;yL7U@z5<-gU=GW14`qsb}mhkJ)C88%%!8m$V z^v%8>V0Q+2$88^TzBQb4JNj0uqxZx*xt2NbBJUP?$6LTp`Bdc5#7o=|i6bxb>GSfI zZ#icP72oq7oCi5}IrrF{{$WAKM6at{sWiuZLd)hJw!%Z_hFf*Aj{WpU%FVa#Je{dS zE%Rul18z)_Go2^hp;$WFtf0fsm+o~*ha5)wCa^5&iod5`>9eGPNwU&>U!MiGzg@n| z(kJ=fDKi3&$OZohoVK91qn^vP-WfQjJjUibHOI#pl zN+JW4vnY`{qc1Ww`>)9OhVo|5PgLn0z^$y8oWE)d5APZl?Eg1o;Xqwwsu3!9C+{$P zH6C5qKAfL2ZjNh^wcgtGPK!p~8+&F2R6e>!RqDlKIyxwBE!c{z@O{qOOyo8v&=XmJ zdwdt~-Q(8Yq0+|k7VB5LwBQr?Uvulh5A?{`H*`JtOh3~TAZ+VRrpJNXP;3ic&K`Rc z-&Xz)WKHrsYuJLHXO(zf-p#vS$X{x2Q)y53|B>hQmAWNkY9RiN8z*^wi`#Z%i6_Oc zf-n8#N1h8PBju*4p4WCsI&A({skFT}P4Og0uw5y2QQph_CV7&u@$_nTiKkyg3m)h< z#gl`)@n`+6_9U1(>39=tiAsCnrb(W7zvy0)Z2TP0XI+86xQoBY4BjQ#^r2$FKvBrwu!G(Aw$$m3}X|3!WB(-uzp%k7oDoA0P>C+*YZ zf0=$ZQikMxd6gdAL3`J|TV1)Dy0%mA32pW-kLv!hFQY4~Q^&hcI2OiTnB}<{SHdr) zAMa9sF!yqW+1Y`(klQtBMqJiqeUF?P>$W3oJrlp`?J(7d=Z+`OdnH_gi` z_gs+W`48Ib`%6{jh`-!X*?43{5Z{O&X&V}HBb2)d8VtQ?@v{^hAHX$$xA*-3NXkvWx} z&39H>X&BE5^y_oRq+0SYh6nh6m-uIoEUc6?K zKa%EejN7EYtf;J*Q=aN1Umx1|6?LsYa%bhd&Uuwp@2;pk%=d2dipoiI%Tud5t15p7 zth^1J{EhzJ-FbIq)x7f5EaLm*W_eaqmYZ^_JF6@2=)9wHe&_tk{~*ps_#esl4*6c9 z&bvDAs@z7ot10s^^W^LFF;*)opZ1k|q`uobZ?C)=e@$miG3%?V89dlYl-ox*fSC&h^7`spY68+x| zo})keqyGK;Z{=U|Jx~87-&Vo~0M|~|?(_8V)Dem2pMG>Q$9rRb+Wr^(3V$nY)5x1Y zr`*%P|4!Ojz<<#NO{w!b7gg?LJb#eW;3>cGg;b4y7yqr)snPC3S3T+}?<}vpn0{PB zA9gYZcXTePe4KJ*Z7=DptQ`l}g$Jas{>A&>7ymM~lUi$tXfc0VDDq{%kDbs1A5mRlqM_JlLD#qlon@hI(U%%4cMqNL0uFcqp=n~YjL=;g{uH-_{dE?u zeM?%;7uiqc4D1fv2>2!3KY!Ar<9;ULdlLQ@TRlw5c!s zmOj0J{A4O+gc^xkx7l+k|JxeVde#y5AYU5(Rg`h!krzExgs-EVPvS;RUQOJufwSr4 zv*Qoo58^La7xGLc&84I*BHt<7sL5~9rU!^$M7)FadkMQA|784){I7oG1tA^WTg7!1%&+@|`NppPWX%Cc=A0 zH0Z;$g{i>mUc!3-tEa$&DZuI+;*){ZNk7b=j0`c@NPHr&SV8(+^n$M;eh6{aToUId zywLEE9Z0yH@HqY}2nY5TUd!CY5iWHX@bAV|gxmR-dXE7cPTW@oc5pZ0A{m-2?~Ro6 zP2h7_|J>E1%?Ydp#X9gR&ax?2%98w&rk@UY`KkCnK5=FNLp^3A9gUUWRV%2qh@7WzXp9G-qW z`o30p3UmMaXgK`%cyzI?@H7>6#KMsSy3zZ!!nwoXJp`Xjy96E_St`69IZPZdp#u-} z;UIP_3+>1gb*zsGcf{ z-@n7r8>usvA6{NmAccjJ@!bV{rc;+TH~`s_JU|-e)E^k`O`yAtb!X zOcD{}4Iw~4F((rQYV;@5F(FfHDa&TF15yAXxP)Gr537X;DNow!BOL z(Tbu1FXEMO5maic)oR8{^Z%_gGhq_3_4%Ifo9CHlpMB20t+m%)d+oK?Ui-Rle|Q;U z({i?Uuzxx1Zxz#@zV?!@N+RJazqs1-`;%XmM8fvLKl@wd8-Fi}gqLpL5V)n_nho#+u ze_C1N2U61Aa!%3#9$!qMfk+Vy;3@7 z6rAjJxs>PIfbx9Q#eAWZo3oeg&Jl6$Nh;o5hz{~R_6v*9V~<6TC_bew#g@X04eE3G z7VfqgUg%&Sayq(C@sBeV`fSG*b=rc8Vhc9JCzWLeXW0EO#jvL;&-g@qDIxrv$a8Y| zIfduc@N*i^L&DG4eZ7_^3CT-*cej&{qSE0b?msDGf=>V@+2s5^?}JjyCN`6EV&!bWB{4NTf zX4({iXFbmmc-nc6!1H^aBk(-Ka|E8}c&7XX(3uI(3(DWbv-Fh-)BoVRiT-&Bnv09j z+z#cR7J+9s&k=ZD;W+}&UY;ZH?Bh8C&q1Ce@Vpa+yASX&2F-DvVn*jt?6)Z2d_KW5 z<(to+@J#vU^Iv(UeDnG5JX5~;{1wlXZ$5v+GjN#C|6=}SAHkGY;i=-cydAsBBy`O7 zn5Lw7c%IA^KSPH(GI@8_OP8tilS^!i6?%H?Vm}vMWakoRN8D0p2R7>CU&arpI%D|v(`$RI@ozQ?|KiRJ;bG^RJSz@+Y)g9sWh(gDNn41wIVfBF31+Yl z5{o^n4j&UfC%Q}FZzeCB$9ynI3zn9sqI2M5mQ?1QG}pfSDC4*Au1DZqUe)}VYK^<5 zURR#Soyh9&GVhJ#W3$nV{eQq=@px|_L}%#59yfqa(91cIfW>N_9SAtAp2u9u^Qc#O z-sYUlA(iQV3!e81d~g@^xER*xUP`W}2x&f>j`pN+r{tEU5g_zwQAZl~@o%r$R1 z@C(g4;dRysdo7yhHHYST6}frO4$ZRz9LqV5pf#qw+nUHZu%U&=z->GUye5 z1#g0$kMS%r!ls**N&COA!Ef*{&?6%QcB;G_@h|-JT`Guws=-e#hu<$%oKsQT)`!QM zq^XD_9v*LIJdt>K%$e~N;^A>;#?y$0$DSF_AP&9C*fZl#nvtUKAS z3di%*w)xcG8y`uW`g`Mr#Hs&2;O%{P5$|Y2Z+r}K+Rz&xPnH zK4l5sDlS*sB6#akS<6|oEXnb5rJQ^Pc@o0R^Bp!{A>xoC>V{Siu{InlFDrtK( zK5il&jgMQ2Q~$JReAMzT8Xvb2kH*KH#G~=Cq%S_IBKWxHEcj@k{n7YnA|8#82Z%@G zhFzzLp(g!NPXfbyy9PKo6I-I;3q!LMLm6&9(2O9Zbr{>6Z}hb7q`H_@-2bM3*ce9 z2SILG{Iff3KQ z38cBH!Q$CR=-grPSVwq`O*b?5ktdFKnS1k$r>Z!A!}G*H`iv)+v;W%RGrC?q$5_p~ zHpbXV?aV7bfgYM&9E9F&F-pnSaQPt=)+W0r}pJ{PT zUP-^Tux7TBM&LX;sv>0v;cAP;BWc&+$JRmES|;PpHTl0O_Z-^Yf*ex7npW^(C5$6H z%J&AsYX0}`y~JoiPPv4%Rm9t9$6)$zH1SUQ{WI{DNBix(k0ETKUK7vM)xdKeZIF6* zQl`Vg-rh^5-0_BQP-#%sI3f>}Aa~fmSMJ~r4r{z=2YK{wr`P7r-gZ+{bJUXMJ24e#a5)A`C85^h>k{d zGoqK#DZ^QbEVfK-%w48!Tz*dsfALRzX1}~k_Pqo6mWWfbo_j<6_q3psGYmg0-@r5Z zz9e7b|BHOyAC@m-`x%kL(lbl_a^^#IpE*Y>a+wdgN9;hya<`=#t@~a0yN;Ye90;B) z$ZF!t%z=!mA&V;HD+hLeU(!#=Foh!TBEQbCjpoeNqEbJ5)cf|6PHZwR#AZn)4RQP6 z+byIUdV9TpJok-ggNC~ANEqr4SORtSRz3KFoEKGD<#|I~_=a-%b?$txGxuq)YF7X0z@m|1r6 z*^wXTrVlT#-)RkMv|}RW*=UFO<_}=sBcaqCkh-~}{8j9mCZ=a4NxiOj>$;_G*SizD zm!@OyV;y!%Y+oKFpNo0j{ALC3;6(!;F7&eKW`$SR`SMt!fJerQ>62b)um2e=8+AnN zk~GSb^K(MOW5YBoKB44n>k-1UeV4m9!QZ}+PoL*D)`K6B@r71oJVj{4&e+XxDv!wV zTU#`Es_tKrK|T2H97%nPEW^q>(N~C!zbixE800)+W~MGW$Y2+KQG~wMBe$32s)el? z+QxL-{;Ue}IkELwWXUW{2Jh2Z8_nynE~JlUvu>HO=%<;pB>e#Jx#XwU?gv+&Pr|>!d^QZAoe`c9gB?*ma1?-V;X9I6fzJ-k{XWk*M`w)b z11IQB(}MxVxX@o~MkzQ_8@Dh=4*{<(_|J0IXr9wbq zK5eeW1mEJ<%6kkQs)Vd-e5^;LAN7B-2H_m_3F$}rPWn*vancu}hl=onDcE(N0}P@E zawO{;^u&NFXj^o8oI7?mmr+*&>6nuhXF?M7O;%9r|&n`muy<@C9 zQ(Zgp>vtA)WO84QjdU0~CMnyYtY*vba%f_P^fmX_Bz~Q@Ba8Fsg%{8V92XhV^Dpbd zrJQBe!?buXOpC7(e+ycCJYIc%7x(6LLW6U_m-yzA{U(_=TxqIE@@GR!BcLOVG|NdV zdAzA1+Lp4VZYlRc=y6e@8Yi@2!UBI927eO26tXVuvM`RI72Bb%#oi6j6!)SEtX}5w zuen=Z*7rxKV-`GYt>xN@t4ROWgb=(z%W&X*E6W!JFXyHb7v*(k-D$}%;Vl5(b>9Q; zCTzzJgyFqC4DYNkycH>-F~`Y{A~Qd9ybOJCG`s?5J@EC<;~WqgZ%fG|c#wR83psn= z0_}+Z0-0;3LtA-^tGa#AiH-dU2eeaxePk|oP7-=7=&!{;#8ZFinO#b|q>qKBp4+4R z{@0cNid4!dDBb9Qo-b7@cp2kS=Ad&D@VQZ-HahSp>4o+*X!!_tUWu=LC|Y!~;wNyh zN>FLeBK%j7hPhzImb*fAlrqQ37?Uw&XKdi7^OP4{ZT#d#CjBtB{s~^b1t%4JSCl7pN!`))`M}lL`NiudzgXl@ zGw^S<@IvOoh8?peKC+|C-weEhqlujPR<<2kf_vbcVU7otnJDiLT9io_pHX)%y!342 z;6nQJP?#I(>x0bmn!ZV~?;Th780=MupldsW_>uFo2(h{X#5&AeC07!&cyD)b%dGmD8A;e7`!A-uGZ{`>IqEB49y<0UdykA!)j?N|sek?|Uh zmuMRA52N$Q85o7e1xBGqAM`DJ;zwZ~Av7-Q-k+YK@er)c15{h95WRe}AvXoX;YC77e5H zx$yV?d7SU`6iGdjN9vKjmO7t+XDQ`MTftoDgx0Fb??t}5eTSN{3>j@Tvf5+tbw#`M zi2oDl%UpYuomqRxxCMr0d@w?4kD3 z+)?m4J500nVH|Yr?b^uk*h?;5?(DnV?@>x zIZft?FW8Usy>1Eq2suL|UdEcUnK?cWn-}3j@=nspm}_Q!cj4b7bFeDP(Dh)(VD)Sr zZ5W=Wo_&ioLJ@GY?>cj9S(+z{{n{@q`a~!6&bjr47r^t!!vByHW_Wj8>ZYFt`shoUCy{qoP|l8*^USkHK8w{oYq70E$`(1r&V7Q6LGERasj+c3p)2p5Eacsq zS2%|v_eab6q=Gf4`1F=~q--6Ul5|qOq`eh*B55VvSM))>Ic7$fJY4S5k-2jx?`_sp zbA4*Dx%PcSo38@STzn68ajtZ3x-08O)(GHZhOAE)Lx0Z$Q#R=WmLcx+Df+~D*7O<_ z^|Ni(bb@Oq2AGed>$)^tmwZ3E>~S&NG-Uj{u)qs|S`iOj3(Cxqmq4baDC;N?Ey z;Prp&+1lGiXvrbE4D!gk2t5hy{P?duTURg_n?6vapS@@9;0i*kPKyfOLYXU0$r>ZH z-xHx70Xl9!k59%I>INI^S10A>vnSYiz{CIeb82|e&4fu zi`<83o6F#R!kPE`d6zp)bsQ9W&SR}_@{Wv}zrb&t$bQ~2%D+8}e-8ig;xDY>?hY!nQ)dfpsOnOl7<}@zP`7-Owg?SYIW^C}@DnZkqQdO6Z1aAx{`Wcw zJR3PH8A&5>712li(+RABZhTeKKT@~AEA?B2K!316FtCZW^G| z2Mv5B=ZR=fgUhwA<(imai#5-)y)18HH*H&ozHB*veE7G^x?I}$e9K@@4ed02axn%a z-_68rjK6>-CKw|=UwNK_uU9K`!wzivpqW!2#>;s_?x1;TU6xO2w^~zIaOO9+MtI4T zRPL{diTT_I&yxJqW9E-D^H=oA-$j1T6V`Z@N|Z7QZylN1#2HBLeGHdXLRqDhb(r&4 zHs+(iJ2!P#VxRcpJ5}Ag2S$vRvWxJ=ck;W*H~8>7BfIP0$?tHg?(qr8eC8Ckx_EYx_D}Rl z5ZpbMmg;$qe)xv_8KwNM;oX5-?-E`*IB@HP;)&>!O03zZkaK5GK#zr=!=j!B{F?%Y zX3}FObgZ?@_@4_ekU8aVl)dv#g+FW8%xd-@Vv&tv;D_j8Cc2W=1uztj%WocI3NlQI_JuiY7yuuv}mk!E?=ccz;GS?l1 z*FO60xDNI1?Hvz?(=JUJ>aJkl2fq01ICRO9wwbwM4{5XBE$VR6p2(b#%f0kJgZ|8U z8GTA7?Np2t^n<7Bkuks1AB(QwY+Ok9W|qr*eE8QrYlR2Pex|Og;304%-`)(+=QrlPmk-4-&p5mZp1j7`ZmW~?ja{0j1YTOg z{)6zh*LiO{}8v@aFQW_%oHmiF~=Je_fWt@4*#74~u5TI#oM*8R}u4D}v* z3+Q+!vW}XrhtiQgl2#?3ldGLkba1VRB1GqDfGmF)a>OtYn1L0$?QOt>_dzk)R|DK<8ePkr0-Y#jM^|Ffqkj(klLl5eVnH`S6SV=@mYXR2+#Up&jbhS4a)IP4dishC+L28o~Cj; z4%60naAl8KmlenRX?plwYWN-Brn$oJpm#GJ-=+mZ@8ZJm1a>J)zHdk8e+rr5$UkV4 zQk#`+(pm9nMJHB$(v@la5CIU}|R`&M+-0a>ryhaZKp=ynrSQ6=jw zAG$Z^q*DJCHf&9SRpv8vOnxW&x=Pwoxy+I+ZH?iZ7r4I$KHq(O=Nn_&^xyh0=|vYU z`g`X^+#UaFp1XoFo6$99fWyb|Avl|IOPNo$<3n^4yvk2omatA>Z^xr8uUj?Onpy5+ ze6wEh97!4OcAbk}mi2n+G5os1-#&z&GSW z#!}iUcZAAao!q5{?>2PCl*Rqcr*I^Y#CVu3YkrYqSL2%*UzexstS1D2-bK3qabz*( zo55aihOJnQ7Z{X{bu_pXU8eaAE>DRcNpMg>|4Mo%V^Gr93l8|7t*_MR`mEDSL;bZH zoIT8)SKh<}SwjyD$rik9O`KK4LaGITako6&&%K^#n82~l-u!hCGtV#(Vn%e z4+^D^Gj;Y7NtX#uHk!29cke>@vCyT^qs$$pTT1=2vgsrGLujR!F7J{%Pt)5!MYk{a zkvx}g^~hKknh@Pd0bzjt4=fqiEoC&b7QojX>rP9}YxIN8{7{N)?YI`>6_gOe?F{(*+Z7L2D5 za3^7=jK!F8@mI-M^oMX^G4YWDKD?B{y2bBhUkJWm=$fhf^SHO9nLQh!(?on=cvoOg z#`tqpYNlP8)a^w+n@T+bqvH=Qe;M`fqONJjm+U{9YT5GyzBQ{x>;6V;gQwAU?q+ni zfp_e<+Z*6VjYH6h476l7%(=O{ioOva0G$6vmu_`WFVHtWA$oB5QFAJ`mz1G{L)Vqi zHgJfqS83bFoF#C8(|PpAK+ez${zrfV!D-8H=v(lq@c*gdn>Cj-iY0Wn35Oe?Wi7o{j9$UrpVA z9#0zl)P7=#>ysvcZbZ&JTu7TcEyv)ZKS$AcKU&;7*6+fyvv|YL( z#LM(8p>I3n4tvH$CwBzO{s?m1L&Do82_FF$vM&<=&)1why;flO)|ynv9aO_Ya1Suy zeiFEEh=Th`V6N#+li0oy+(;TDoMt1qku*Q0TKLNCI>fyg@Dy7K^aTyz z-Ot?n_1*haxZ9^>RHO@6kAq?CWpGravd;-p7=`g}KCud{PN46^AOi zTUK$;D|j5jTwnBRkz3#szw377FyC6`e~r3)kLiBwCH66%XV^zGo{>j<&w$T$A=&X| z#{+dT^W?_Zn#g!M}8Kv z)KFxpY*UsJIZE2O&YFH|F8DYCzNMW<;ZGI7G7q27B2Q)Fzgl$OZrUk$eg?XezL)lt zl!G^T(02OJ2VF=%p2Gk27Gx+d?e`&1nfQGslurCi`;e>h;Jwn8b@-EZ(nn{f{m@!c z>akx#`QXh?eg7`4b6Thm*2%a-);4J_YRvq*yr-9L>0={;ob`~56Y*Ogk+WpHMC2?P zSEl@bGyD)4<5$Qx*By)VJOVtjC*W(+yA}P>49_z4`Ux)oM~6-S!G*fWTkr?L$E)Cd z7IH=>b_nQTf_I!)vVUt@`ko;CU2!M9mocR+*Ro|DqBGu)p*Qm}7VA=!zk+;0WW1Lt zclH+c;mLCyWjhnW_cE2em^J1dag1Bir6b2IN<=3|`e`TN?>828iw%;wiXxXtUEE85B6ILM;9tq2YB2&7SjO~`Wt^!&D zcJqE6xeKNXc~06WvYm|aPw=nbh3s@xe-m z+Pew4Lu_Y6rV{%HNuv+4y3=Wsa}whY8B^q{>yfK|#n@~HhJ(me$YC>QV;>>&C$^vN zDPg(lSJ>MXfagb$tE#YJ6?w{wU5dz4o5VjDZF&TGs@jS?#ka0hz9C1w#-2<8dk&8v zM^#yoqxj|&ISl#fQ}$r;!Q&&yPt{iBr%0K|O#}H>0DO-iH&t1Yn<8Z*FI_19!WjRL zATL#0k(c;p6Zr-?>1y=-1;G6Xa#9uhHu6oe?=JGu&sl?Ux-$3(^3hT&@)6%$BJVLr z{)+K-DQhu#r_*-9k;p^H`{q3k+u1WJBuxWrGT|pTa>t&?LqX)BOE0EOWSMH#US^qJ zMdYCD_Ezef4ebfOTV*~*26|Q2QIs!b<8QuD?un4{&!_*T|HRhG2G5Z5?S$q!On8Rx zH}pL`)|Rj%OEp@uZS0fu%0}?i2*0->8%bKB4dFdL=tgL16ta}imfY{q!#wNYo2)~G zS4w|xC!NSjA|KTtNA;F1b4e<5jE?;hw0kzW=_oWLa?_%J_NW9}d4;*!wP2#@kV4RPil>!xr?7fExCtAzE>gNEYGp9CgV=d-@~6xc_qF5F6KYk-yN5! zg2~KzQlHTOtE>r@laBdy@bc#r`CKz4Z7Fl3h*qW`K==}DFMShAaJa<@cC2eus> zkN|B**A=uUyZj3KXn7tx3Z2>WAvsFq!76+ORN@<8WY=xKk7>9S_H=q&xV0`axb*o@So?f>JKfhC)!)G~uk~ypSFftF%cAkB2 zK^Mu^B;Oaz(N)7|EyfqAZ$HoTGG`%UqqE`ukJqtn;Jy+s?egtW{v(ycXDMVu-%C7W z7vgO}9@@$IN!}xu23wEe+jJOZZs#4}TfbKR=66Z+M{spg`9=Qqo`h$PWdCP_&KGr32hQ1Q-tspc0Qg^7P~ z_z(7fzo5)N-ay}B?ztGK}#%lgu??d|&#Qrs_W+QrvPb{5vxA0H0 zcGkHV6HatS74g`%LzB&4>;6v5fbvr2@QTdt3C+hsbXlsjq~^oOg2_9wUPCUH_UX1j zmW8^6R!hD0SrzfB&XvggC*|b_>PjikcPX+2W!7W=BxRP4pv-rp%6#iTl$o#Uuy3JF z(0*BOmK_pZshGfCBLrSC;YD`Swm#M@ikG6xK8Z(GhD zDl)^9zd`mzkFs%u1aXLet0tLH$*m8<{g` zMy!7qjZ7oF3Epml?@s7o?N9rAz@L*)bjS3?XU!-3&U;6YHG27f_^!8*kCgY#c_3;W zl>eu3fDe@N$%2P7{aXH?`t>VoM)@zv6Gda?|B1%_VI5j7@?l+kKh2Nh8KG|O~*C)d7+u-$fc;0UKzwrDf(xo$ZC>uIg z?pL$9(z9hA39u(3V}>(;gHYuZG%JkOXRjviuIH?+5n@RvuC@7OoY-3&pJ2@E|FpWIF4tYk7awnY}~BdSo8yI!W_G_Y6-jJ#$yE%=5A~92VYxs$^|A9-Xrz zHGR()Gv!V=i#wPvcL{6la{k1wnAgzX>`&p|b@qH6@Aa(Z4%49X9`)F7pV9r%Yrx&W zqHdhn?PU$v9bT8o9U%eI=W^%f7S@6pbLzTr5Iy?~)@ZJGgm0&tvOpg4L%k{6aBmGV zkH`eVYbA}WbtT;ZmocCp{pKpwdlTx?wq&Sd`L%@)t?>=%l?n85J?OQ=&uJdk&d8#DZN?(@Vz>XB_Pz$q+{+v*?G@Ohz0z)h zrzIvfslMUSj`^wgX7#rJd}YG$ATZQ(C(}2;;o@GV`-zJW8-c0q{4?+zLkHF$rU#M1 z1av#%a!O@4fKOEp$Gcm|Iap>^cU48 zX`{4D+9YlJzII8Qq;20v1Hj(9jF=JH>I4oUX1Mc;sIx=(b!qVHL{bBt%Mbu2z<)sUm;H)Q{&^aA8)zLkz5X~AAq%`FUMlwlPc zP0Eh8qwZqth#o}l5_dCf@0$&efOXJw!LZc;>wHQ3-(=Jt{%n=A0{b5qe!NJh)V;x-MYRSZj$-uhJM*rzFl=Di_^a zX@aV={U72f#L=gfCaXFfc(pX*;!8VK)j26&%ODO8ONSssOZi!P-8uN(D$P`NvTrQ@ z+u(17$H7TJsqzHARdp}lzYd*Q47S&DhXQwjabK*p{c)G_+{V8P9{`?!^8AATua$;B zXU+4Vu6e|#+XF7ma|{0f=gGS`^Bv&qw#yortaEm7kQoJ76115slf+YLb(gS+17U|Odn`vhwO_BkUjI4EngK7eHHDD5kF_( zDj)m7DS|KFTi>ZNeV>(quYCNUIXT;9z5{1v;4B~CXMt1Uci^oIyyfHf%ocfv&an*K z+3|Vi!e5`FejVJ+fCjp(sqL{1*z@kd&%8zRWPlfoL-T0RbLx(eKkIVj`Q^Z}X@$iz zlJLqhxf>k3T&q&WxAjckt8n~k!bEWNMwY`^3Qj)4uk}@gnc=hi;y>W8*;gCl$NI{h z7SE?QUTy4{>oDe#Mu98aH7fPb_{7#IM|@s;cUnEuC}YUn>BjCK|Hu%3)*9)YT-J3x zWmrikzN^JwwRZ)6qlf>-ztl0LP360{41d&=CBCc0 z=XBHDsRq6rFJ6IP=nndD6K&i`{Kxop7C+MW;M4YHLY@CG-mStf^JM%KBZp45m8;b8 z_`r>&>}q^67vdZFo_FxMOIcfhX$bL6z%ZEf+wj|b41dGoWBM|D7;h!rd888`(lv+g zu6>{IIr3}*j$04kYWh9C1K-qk(l5lPv-qs8z~{A-v3NVarXQ!gs`r-GCPB}a(k4H7 z6DW5R@NK>|6TD9ZKeZ{kG{X8_5%FViXY~QhwrT27WoFwR)273?LmCtif?P5 zS+3P1{(m1QjrhF&3SYT%!Jh`)58!wA@&9@%#q{|MtbeATcJWSpVrP=Z%nv*}@h^Ln zZ{qtp)K=OKj-Qh9fomiFu*FCA|AgTXKhWYU`w8-_iKL_5n|MyAeDHX&^g;LGU({aw z{?BUv9PaZc@RNNK{0k1nXQcSD7F>!yY&-u1`r)g?Kd;?{Pikq0_|O(Q5&zlZ7rPlB z-FDis8UNUJ{1uBYZSfEO4F0p9#&`CI^q=GtKhZ)T;@?~FcoMo0pUvVUdhQ2z)jku3 zL;QRH^>A&i_{|RC0^i4nZ?0WNy4~b)ymyc3H`)9~J08pZlj&dkvJY;nJ#qM^+QC9w z^pW^o4)sNhM`-X8aQ!4@h_7k!r7Zrq|LcQ=wOxmuwb#;~4MH!p&qCfz#?upolK-dp zl75`O(8{zC%_-uOx|#QX_sIZM_WiIaX~#1^kH&MW?%(R%6q z#xZ;aKRWSoBZ;}>uSVw}K-aPHzY#s+){dzapClwbOQ zF>2xp{0;xkNP)l1UHX9Wb8ym(f8qSP6~+S-A2MbTf3WT$quk<}eEd?EaSnBSjL+gn zt**%@@m2f)-|dt;y?BMuwbW%?K-{r-g)xAz3BSfCDYp@S#xE0E@mJhTsPI?ZL|Dzg zjK7WlpE(y+1+PhFuD?~6xj*p~^2==Q13Pt$`@7{XYJG+BC`(|nV<$dOnLCo0ORj00s(F5eAKM4t7X$O-hjnGCJoji-L?DL=p@r^x*G(UnCNPEp* zp5G*lJ4ao!e)UG~Bf#f&c%FNWe0wS53d*m9|4Et?tJ_SU`d4FLD1LpM@Wb6PMc0T= zeS8@Y`k1oBpY}fTt%nxd_;&S9&9j$xuk&ssZJ0{kEu$(@cH#%-McP#bURy{j^1+9c zvw?EjDChU&`2%_G<=ay{Z{m4A-|+K#%|qmC;=h^n>-nd`bAkWjWO(Vo%Zbz0H&*|_ z7(^JCv)PdJl2+O}XpPtCiY>aPlQw9yLEtKd4xH2z@!2|Z_3w>N?lhBccJjone%c5~ zNPG;}dH^2Ctm1h7saUkvRP8i?dASiQlh=Z-dcFL1@IUT?T0BtDM3L&?kfVY1Ng znLKU%^0b(F@Z*zWA$=w3t&EE!-?JY02p_EZ?4gN#?&Z88viBPkpTy4u?{j%Ca=Z(D zy!rl`X~Fk+Z$5u9HTZVq`G#r1gFK55T4WtLi-ApAY+(#%5I!RO5%nW0?bB{`E-qbw zkHiF7cUV5hj%v1)XW^T=wUTe&Eatp-wx(=@ZOf9=oeT07J4ic^wAh-@ev`CUAS-uL z$HkO6pJ$P$FW~v!@bh^*w}qca^88}>IiKeb!_PLJbMZ?Q`A7W5M#6~S*hm=h8yg8D zeq$qH#BXdQjQEX>gtG5vr_B~@^<=M1Y`$e*td9sXrr2)Lsa%L2rHb{wS*}g+o!%}wimetcN%T5WhN~gBTl&Mo_kX^BdhJ`> z;pC)DS@T_hjYscVIBHL?|G8LcqqJ4*rz)V0$huedMMXy>`*>fmPLcKMch1FPhmgm* zHlH=E#&=00YuaY)3cc9QDC{7;d-L2wZr6iFn%e3!*TO^FC66O~{!!jV^6lrDGqGD+ zOg#s-TLRmbGv^;gmld$6;9BfEZbv8a4QtCABMyv&V%56;*QFWiBi@;%E4^Nj5#@FsdYu^BI!7CH~Ke9vXYqSJzIXI4<2oT==L z$F4yQuZg3MwtS06&c{T~*mj4{*jg#4Ief;p!fJJ2zu49x`a;nu*~4dS*RlU%gVy!@ zx3gql<4W{T2RUOaZSZl%@+kHTQcvWJt)!FkCG98Z1tMuBot&|a;52fMYz}=PeXxUX zChi;QH=dirxNpPeM9L7`JgKvcKATFO_4K9Cl5eCC)v4pqg=0Vw^itRugF))5p6j*#i*YvXXBGaurGr zQ#WyqmQh}xtRWwb=RWTk_sLQ<<0SV%2AHEx(k`3DHMs{pj81rhyCLg&kKNepYZZ4K z=$dDSOY>YG(2(C;lONKtjgj=|9wc9YzsA1{`=@JJ=UvYlZzk)x8LZ*V^t40L^q|Ml z32ou@PVAnpWqo%&YrC1O<7TjiGt*0(B|SQe==5Fa$*$!*(e-)^>0?OGI)QW#(MB^p z`i$uGPHdpARk5UxC4DUEW5ewar$^TjoxTgceyxfleH`iINFNt&e>golqUiJ$jQJTV z-gB)U@0sR`_c#La9vkfuosJ^x0*5+(!DSD37PqOmq&O|E+>4EDtMobemOgI=|I+8t z{q4eb-63hfF@7c8tiv^lxT4BfeWs02(pdV+)sK`))a2 z=)x?gAY4wtcgt~M7iX3;DqPN}@0R0)Ud(bX2$yrgcgxYC5wo1p;c`ZQw;UVufLTsa zxSXQzmZP8@vz%hJu$umAEACrP1O3&-dZ>*)t+d3t=O@OxTj)zgU+VG=KJ;L$8oZbP zrokekCD)vXfBG8!=Yx+n?wM?ZC;fr^pH-xJKGL$>PD`qLfhEDcTvMlx{^OqgU){%9 zZ7tzc7k9tTn}L5Ud=Yi6mHTQlZgolfoEA@vbrAV1W*+I|6K0+m^RDJy+%Gvr{QhuX z1~Skd=Ek&nT1L(Fo;6v|z1=hJBlg&ZpPgXu)5{$6(QlQfnl>EO6!0XNyiRDMWs2$$ z{QylY#IATbeO`g@7?DwsCyM?g`-q$)TQ!xnBm6f5KD`=S zW;s_Zwxr7~L(DVWPVkp|nOb-Ybw%b~`JTrdI-WUHXa1D5az4z-nW^U29YGB_%EtG3 zN}clI3$0fs>$A^(#<#?W*0{nA!2Y2%rm&4riM+^MGoR-^bJDHMNqNl8*rsUANu`G> zIx3hii~6U#iFC}debO<{j_jYVl61_?ebO;MXZBC`Q_{&f?&x}%*Ax4vn@c)mfj;Sw z2fjr%?*qs6q?2>t(d8jW{I!3&nWRJZ=#vilqllMY$uMba&NN6tb2$=nZMuPN2Hap-kAdZi~Q(f&s+9RKZO@O z!nugM(6z|ACx-I?c0$P`YnX;1oQY#?Q03G-Ych5JJJ=}7x2nD4-5TkRS~Ci(x-`!L z-uJMs36(pv=2iBCT}MN8{G7Y0>QB`BV@Y2xw&2)4oG_Jrn7()>2YW&I~VupIV4&2pBoH=3!&&A*9qm_Ms8 z$L0)s@%q31z;dn+m&3mLuA!83E9Ef9Ry#DmmwCDV??14dx#4me@CBJhIkl9-JX}3h z^E;WB>%aPe<@_{UPLm48q*Bgpl*3$IeI@u|UatS<2bNPAF6RLiEEq^RcTx`Xd9?w4 zn3wDS^#jYfDO}F4Rd5PECFU=o9OnG$nc#eIy<`LG@|HN*i|NI=WA@O0O)aPsu=d!=GQg`mR)`c4F5q>rc-wFM{>BtXGd>eRkESxDHUh@EZ@m~0>8l}s5u_7D3Uo?Cy zV_Q;q5MOu)z}I)`#|MI!`b|7gzw~cpjSv4!YfW*@jONQyzVKfSKK&7Ch3BtRmYNU2 zr|7IA`YN$g{|B;%;9PVNU!wbYit%P6Z!>nG%J#omD@Ji%g88-nojW?57>tTd=_c^} z9D0s5uZ(vmAU8Sq-Z@q8a7kLF(iDCUOR#^um9a99`5_%!YOzz0vq(p%PwW_E%?z!r z^0$!(%P#d>$?xq!6_ZI9!kt6&K=mh0V z(OPsBj~tEhys$FfvmM*_dGOP3(F=Wnt;yBQUAj%pu#F02sS#?{iX`Q(dIVd2&H=gb zjespc`3UFCi9Z2f^VEo%J3ns_hkoG?w`H*kjVCogod!nTujfwkrp<})k! zsU{t~bMWJ$N3kM%1EWqT`lc3iD%EO4QtKgG$7SS~w*8zoN4Kepvt-gH>L_v|pG%t* zZOTqox2a_@={j^ePi5Eq7MtEA)@2TrU49ulu5~?}M}l5ju; z2WjJ6;tzh!I0V0~*mueqFKHKRfS@%2yQ2~MM)XwRlKYTyVm3f;#2k`BK^yQWt z@Rb5SFL%t#8pFr^^1m%ngT=naPuc?3 zBwg0woTE%{A8gWX_XK-7{t1SG$8Z{OY^HgNGIjWflqcm1F6E4r^h=}+MHz~Fi-eZn zB)^m|^&GQ~z*cg2dpBj^8?kUiPfu?>tg+0tMCw^d8p`NX&tI?)k$U8;u;gzdTmk)A z3ZQA$>dT=~`{or{Q$|Bu@cR1X`lQm)oFxaZru-;;F(N~>0O?`BLVZ5MDvep4?k$mZpEYIOh|{*t04L&GA;6e?moX`Nz2WSW6anjE+9P^F>9?Va>$<(HgGVsV(?{vSf!Te> zL}Z*=hxZ+)Bf{gDy49 zyG)*dFYP4jeba(J36IyRLoalAm*g*gO6CjjFc8`CY`h>JpW4y9ASTQU&|S|q#}V~B z$~cOYE9Ztz&(vNNRYo$!Gj(Ccpa zfY3vP4|GC{LQ7lbv8MqY$-aj0f>O?$M&^aSJ|$l@ZP{L^ij40pb9@h%@oVXxP|Emr zFuwOdyOJg}?y}mS#g~FPzW>1Zelj$^FMxN2#&@WUgwWVD$M?H3F35k3dA5~va;=gd z-%gUZnKD{AODA%l;CUQ<`5HJs8?JYArP`yhCahG7M5urb!9{s;^|!!A}}NP(6@?aeBzBVCt_<8WI)rk&ESM>xN^^>Q`% z1aaYAQns9z6#j^vz*_O$7s21bs5Bw|men30jmh7>K9g=N>7v^>mo)0DzHPkVcwhcD zo;*8$0Dn6i;cs8`6m@+Mf0H|lg})`hr;fniy5Mg@Q{MM_#vO*geTb|a!pn%lv9!(h z`5C;#(~f%%M)@?mBx)}*Ntt%wk$ppFp%y%XO|_NvwHZ5LTcWac|kRB$!68ZYqf_C)NBGqC@~#~!rj-<}q> z&rcV-{GI0biRdqcr%M?shIt-2R?b@rU(aREk-H%-gigJQ=DEv#V!JQze~z5)Mdm|p zAFMcotcD=pEx5f~Y-XRMen~H93;u$fD(U2GjiftB`y}lqNy9lZIWx#UwV9@OtRoMK zeAs&MH1hU#bKbF#r>p?Jf*&?JdRCe9PA}~=;n0~6<(W z<(Kv1>8SG_kv`#UpGR;fvS($AzOgzDzEGz8&4a8PPl`+)p;_=%kr*)TY3m)#*%D77 zjt-=LD(fitZABV!bQ$$m%ABrdJ2N<6hF;FZi`j24n(%7iA`h5t$gVnevH`33t=kr* zivl+0;eS9gviCcbwv{rk`QW({R*o!XO_ttXpQq|dM=>v7sV0JxwLZpUGjptMh)QZm zK_3X+oT)oRGg5}&qU8sS-R7O7Jiby7?5iWNqS=F^gH~80N?23QL2y6n6;96)?@GX77`mGO+*@rasQ;dx|!Eyam;^4R#rmZ zjw{mmPTXZM7iXxt{%}fPvyN+LooAlol{(Xhlyc@1zt^+_8|1<}9NZI)t@jc15zU`=xy{zQwogKWKBQ%tiNv za1=n+7u*P5crNN4e^GjqiX|@pV%l_XxSdrE_<-QNis)tJ{`n2G^#p6e4>>DjFJi4v zn=5FulXS9ftzv91zellOpyO{()jYvGCv%UKE4VWCJK#w23ZA-{+ni0T-@sD^@XC7B z*~EAVx49nqUfLt=NM^p1=lj8(oFl3rpOrmf!BJI%GVAYRpHy(}qzzK1;JOJ|1O{op zv|HNnQW%c2!BB>bAZ-%8r;YYYeFNAFN*KUC(h}XTQ@-@gE!68!YUTp!u3RE*KD#c; z#n^cu1HQv}a8uT5>{d3h)>Dj&TPT;%vw*Ub@ud?FFMe>S+Vh|#+1-%B8g3-@jnjjr z6ZN12KQ(H>&E0nXj^sUAGQLCxDO)xmdn#oew_3_~Q&y$2B*`9G75}*8IJfMD#o}8d zc5y-XIfUmB#v*gYF3#&#$aJ|X?v&h{a}IGkaUVQfY-r>z)OD8RQ=Q0&jJsiO;P@qQ zEZ1^t?gfU|kXZ`rTz+YD95}i166Lw_^Sk%^;Gy0Ec*_Ow7S7z3B?Pi6?^oGc^2=tM z9jUIY_2|lV+N_d0MfM$Rt5@8Ek?6Lg?3O;)sivK78xx$$w?W`LwVicc5s_Z^Bh)5HG-SY zZuZKc%W80wOj*YXE#P7WxU_;x;ddpg&OTxK{}R*+qRe$zzLS9trDh`pV(qbfeM1rP9anK{LjMR0$$4)&mCLU{HOgyFsf5n6swy~vmt3LZ4Z1oP7#<|en7H7I*v=#huI zWnAQq=f0{*z{a?6EV!e)o_|&HyIG2Pa{jUr+1n}mQ*<5EbZlAq=c@QqqX`&S|0&jZa_{hc*pVbE>^-2v^CX0xrVxGv4a)e@ zV>>Ss8au4wYizOgm${%ze0j`XrzO->YY8Sj#vQ8e9(kysdn*5%$ah$H0Q(MY3D)wL zRcul_W96y=$`g-`jEuzx`JbDbyhqD86$Hk0D)rRI#MAkH7(IRr-@DhY%Q~#3pZc2b%e9zOr}#e8npU`7 zOFVTRe)Yb2iMVAz;yNw27V-l^P*qYyfogR9)_@B*~-2yGO!T(!ld*73YrQU%<0&2~1``fADf`V9j}@ z$f($e1XN(M3;U2N_+O8fT(b)v&;<`j&|;F>Sa(Z1qz#HT*l5FYaDO#(>W5Zq`4sqp z58T@V`EENnRs5mtQ#x|#3Ef== z_?P~we)VzumD{cg@h=_TC;Urz*S%4^OYG&IfL69cJ1;^9F52CqtmQi8>4);WMNeYd zMIx^wU!3avko)AAvr3q=N}7}(UD+_9!DEDCk40I-1b;IKW58$JV(5-}V=8~@_Xxdn zj(_$%z6;NicE>EXcf&_L%V~@7JSj`@t$wkyyR**LeWcFWtu3*211CIG)m%lIKaj>e zmpeFW9Tw(ovJR88xFXv`_BHIZNn~6nyi9CEof%rlE;MkdCty)Q&ajSijtTK6CwD3? zX1apuqH-yz6D z@~wgT1urXTkF=vHM|@IfN%Luk^m&=^$7ONZZScja`^Dek z+wAj1_I2)}9!W3lL5?bXpMI;bgyt8iGkT8khQA#%@2~0mP5R?^CS54iQ>NbhD}5;E zmdTVrk=6wugXQ5%Q2lqWsF91BmpdbUbk$#_k^PNkL+UP-)&Flm-bwGhgg-a7~r zbbJtF>mKjIzdK=Ez~Z?nbG7j_&(rwFihWzw!`Rt#his$Mjr3?%~@&0Wj<`EIS(7- z$X}ne##l3Vjgg+&V7$ot+e!Doe6!4HFm57k)xqbCl?S&Pwgb(^^9R-&Z3jjgRR_ix z4<8(7oH%GRW*?kjEIK&euph`X?&5ppdzaKccVMeA=D-%xjx&z#J=fU%-ssw)oXN8v z7;BV`Xf{sp?%}-!hHG!Daq!?6V|LCnM&5yQjLZZ1#y;L1;G6wGo1qT07%c}!8tV_P zH?H99$l6IOjYY+e7#W;FS-E7T;ouy}v&CzTJ0`6$sun+DD9)i&ELmf;EnaI(Eta${ zW6h+8jnzvYHk^xHq+e+~Lpg2aLw_)NXYnfI0A~$$FJ5J=gqN0*emc7gfxpXo5KCjWUQbMZlfW zO&>Ie`(r+RApJ3m{up!MS>rnJcPIVv2Dq=HPvp+zE5NZ6{O$z*3&HUlg!ThP##Z`Z zDt!XZqZFKODE1gl z;B@n1kI@KD=Y!8Y((fm|opjO%yUF7r?=;Q^HIi>ExOH$o2;1w)`K14xezB8o7vTo} z4g9P4m+}uxVD3@+wi4Dj@d16Cg?k!fb$wf$ljkmdTZ~QLHrT6g8{*QpWfbS-JXCyc zj<5KVoSnr*IhIj*IR{1+UYd@rNpPQ|6+YYGQM#^(g_9dh8 zbL_?WIo85+b7qe!$cdkHZjPh4ASXjt+u(cKVgqWM#ieMY+NL?xHmgl-i|I2a?kBXF?-5w+@2OmA`}4rm2229WXyBMZ`vn$he+%$z0G44>XnPDWGy=;m zwJo(3II4l80631TZNBs2?=RD4e2&IW*SEF8-{Th$Ptdmw`v^Ej>DyAD0G9jpZPyo1 z$a$D{`HDy990TW#wBd8=ewIEFI2@xU8>Nqy`&X5Rwj}!Ak@Jc-!Q+1p&%H%iJ+;gM3*g%~GY9;)vUmm}Z(5br zl-C9z-=(M&_RAC8@haZUQG9m``8(kiJp#CnoEpp;j@>( z1KQpz;*92)+Rt8XF)}FUDEEjf%H0ZIZ5`2Oyu?{7J9n$E-`ipo5N_C;Z`{dPi#=#J zkb@>`2Ytp%j7^=qBW0|THP0SBd|up8G%vY7dQp(H1Z340;jTM`KKW zv~Sdy@9?9dA!@`d{?`?c=`)6_5{lqQV{`D&IbtOL|IClZ^x;P{qxjLxIp_RIc#`lU z;Yr`&N9^$lKU&9{T=>!1cv0GSc#*&o;zfT4uJ7kXd<(;Jc3w2*{}W!MSf^ybbB4Z5 z`=`T87limwG!GJ9y3*vO5gs(oGOeRb*Q49G5B6-k!P2l$#dK zS!G<8wbr;|?pk9l@|BhNYUHXZ$YT!Vt_;%o`2IZKxAJ`@@7M6Yl{B+6R~Th;Rv2wf z&(+RpYOQT(dcM}y*i!pk({qNs5gy#sYOHN~-iW!+Yd9L)jN3?8)cAaDTVscDU1Mu) zEXw&n^yz`N}W)-hS?rlVdU2Qyr z>>H7LE0DVs=e1jqd8?PK;e7T=(w>ofbz~`#y(-CDMLLmtTZ>m2x5L-k7K1~JYjPgx zt%M?X9p}C9K;i54{G;XGYT?uL(?LQP|1$p4Pj}E~BE#NEANev}#wzeC{j>ty&PJxa z0(n;YV*-64eKK~=D*9yweYJx12mMChNWVPG_v?5s{UiO-O1ftHW3bsb%s=!?bz>{%nWb-@ z;oCE{4>hefnwr-4_DvpbD4;FUFX@w-kY!gJY2a7-Li#6eNt4+pn~PT&pVJ@5!GFu* zRiq1@pHG|Q;atAQ2=z@CpRKhmowd#xDNl0hm;((c zpJ}lqU4q}nN^M~I$69iEjW#gpZ|IL4YCzJ@R7%pdmZb6m%b@bBRdRX0W=;B~O2q~s zDQVN)39QZIvmd-WF1u=3Z1#C~$7b^`y=qxP`Yz2{-fVA3ddmJ_(#4iRNw1ObJiaZ| z1|_9X*Dtg|dAP9CsJ_s7_jCoM+z4=&KL5ww&KoDza7rCRq3Tms*KM)V@B*jZ=;lD24; z^39qhX@@qT{O9P%9r$&<1{lw?q?Q|MV0o2=w$jeMVfZ#h!N)rjz8%0e+LD^|n*CQM zE`A+`Zv}8K)KbBlC8;go#lZIwa3Mb@y{!8;!qd|dfNxZ&-v%uZ_>#&~r|bSu-z6nkp3wb2r>~OkXYU&v zpj=2gswI{GMN0y{A;6bhJ`32+2gVh^R|9;~ck9EjEx0Ekdp`Y_5QfhJeA6t+Npbcj z6V8``uZ@1)34EW@|24q(7cG`^%9iqzDz)6A#U*{H#g@ytH0*f%@%IDQbmd3B_9s07 zd<%eY6z3H`;@ORU&S0VO<6tDnPa9hN0c$QlrwjTTy0Sn zcQSflk)=hZ7M-Zb+DR(Ot;2^JDdTD8qd3Bu%s;pDZV~g3kN9Ea?azq6#Q#p>v5mgk zwT+u=cQ$UQ)f#<<3;yy@<7ShmywteCINpTr0Uk51@wwW#rVX{LAOFAXy$O6&RnqW% zx;q;rbaoPHAl*q=5*0;26E|)G2qxM{$ha%r32FpI5m3U~2?-!DYBb7>1EvE+Augk1 zP+CDdiio0)!i+kOqX{m6xUY#L=KI&}dr1?}nfHC(_xnBHd{6Q_zuWiRvsBfos#B-V zw$5FS&W=E@>_C63HIKW?(fc)P{^Z`lGkWO*=!qwEy&^hB5k!B~qu(DzZz=T2sg&!Y zzV(z_gZ_8`opL`qKz|%tzUA&%Pk^OI)s%yr! z_8Ind3{SDU#Ri|wZ=pv7L1-a#DBx-0KgYoTo+I$ztm7}Z-@SA*{Z0sf!F^=S<{8yG z{^(mB|CV3CfAdlJOW$YWKit5-5Zr_YwT4ce1a1*w_{-SBOCK1*f87l18y$ZOPqD>K z`#T4o^I?ChROXC=+?j(8OhU(vM+eqjeASk#(Sf2H$5<`JeKe651j4Tch9M(y9?=ih zSc^{!JdUh9%BShbP_@NU{9De>61{MewNLSRDz>-)x!H*x72V)c@v$db`^2tV7_Ivz zr=usz7xdXT>^y>Qxb5OWTOLI>Ohtx1q8^c< zcIvFA{sf;2tOS-w4c~Sod)0&JLq9U(L4KUbQkx3+SyQO%Kls_Q9ep051?<^c;2?Sp z8$IwmWkpQY0RjRo%5Yr(CCxN`6thCUVD;G(Y(ydGlyArn2Z!4gqC zh_!SNCJHaakyoMThO)&7P&;o*!;3hiHvRn%Y zUJoAwuTQ~CaN3T}4dE51!z*p2`;@_J2sp0beIM{&P1x_OjV(uqktcSqvpV)F^jZ-( z4`B{vsiik~MHH{F^w!%hI)=G(>b5NBeijuI8#O@%nmBK*_fBxzNPAU)+fa1iewkP7 zw&Q!D|31W)w(53#uUt2J@FV7yp2co|iQijz9^m-|&kW;?3nqM3c<8c4ym8=0mGFtJ zS99Za_>!+z3FU+Xw{V9P@dt@NFkf}P#P0*FN&Qsb`MsUAgGjfOu#YfdhD!L1Z|xVU z&iz9ouRkzZb$)VE)b(eeFFrjj`g-wa-;u2*es`IyXO}zF<$l75O`nfZhxT5kIzM3D zY%dj2_hC8f$-wEOYglhGU3Koef%0Z?PaowhApD-Nh42>PdqOJu2Z23n7U52U__<}x z`%rZMkf5p?jP4)H**=3bOI4UFn~N=Jl7GfP4~H%rsGSzxYph5 zXmAh39=+;f{Epb8+b(NxKcBwB-Foo~ckknFbuUX_>+W^Qt)yM+o)pkm%E2te5<>PZ?}@>`kIY1UO{ii(q{~-ZJzN0ddy0nkyG33exUY2_sg{p zxhrZmx`X__vS!naF|`l4w~?ojc(rB={Y1dMVc9co*P18YQTSjtlD1~e({9h*kGXf; z-Qu3nf4%#I`pxbk{Tto2oFC)9`w4f|-G6dFaQEZbwVU*Q=!Ml!x%Z(nH>~E|tC~mX zM;>vHp`0fAtU~Il;kkpbkKiIGLV0N>Hts0;p-~g}5ej*);prkMLPcJ+`yp)BoxjR$t6b$CRJqa}gROfG=?~cbX9I| zUM=H_T6YfmnL7yUw&yK(k5;`7j^};H+~sZyI$QkeAMh@Ibf^3 z#_d%}eVtn1T4dCsX@L){5yd}PBVxB(V~Pi<=;Al6G01Lo?A6Hbi}VHiu{obsw%ETS z*B!|2R-S+3y_No<#p;NCGk!@)`r-t~tHY~Gt~+&k$;zTtCDSZROYU8q=%`72{*j+(vtTuFxA06aD|2SFOQw%M)vxXRZxo@@>=RDUAm=?t#HK}PfruLP=~$s%WO1*ltgp02#tQ9(ZiCU^Iq33m+HD61p`Fm}DSiv> z>Qy)GOgdc(&BAF_b3wdLBcYYhY4fyLopwU29khecN@ymu+5l|j{QfmMm50$P=9g#{ z5$bnh#zLd*(1^Y!asxD)3XQV)RtSyuLmO=FX!?{b=-;#$EzqkX#7mJowZLFYbaBi^ zEzozU7Ra$g$66+^p9tLdbtz~iycP+c3C&`lSxl_(nDE9AU0UGp@LY?*Z`asjVgum$ z^`a!ls}uS-p0cLI?ttIET#(?1DTs1RUYzV04$Y$AEuracZo7k%wj51k;i<9EYC>pW0akYN%pHi{6^?h!I*3)Px>$J2+2j(OQio>#WNfGy}exP z9F0wK;2QQTVaG2)mmiOA78~87B37P&zCJKr>s*Fyzn%Ey=y)g3T5SAWzO6uS9AJOV zO7y+h^rnp;sUq2@&rnhIUfB=Z(@P$)rNv)<;0CR8T!fk^{s>#Pb>dxmeZjN7Gwx{TkSwqp;&kYg*hV5@%obl>5MZt@HQGo_1$o^FOn+ zWyTQVt-K$&OzS*t*(2^M((GCGtovl*3)eKe53qmWhx;BxuRr8&!T&Ry_*wUDa35fQ zz;EzDE5-wd20n?;96vs~{Q!FnQdd9Vo;&by_gSkqamHEjtb0~(bRP!{Z{7Vc>04%W ztbWv8w)!#mp4Ioe3s(Qh{pQkbl>MYTf0@|hP44t%>(Tey(D6^YXDwat{wpCr`s|7I zJXOTm6VsxHPb{RLm=!gAqWDNQ^UO!b*7H<63nPY4OhX^bS!qsw|CMJ8p_(9L3>WJV zU0U~8LVWizj3=6S))2&xFTNjX2cf&rT-wp3c^bddpMSr@RzV8P5 zyw}i8MXnTV<}^o5T%w~7dTC62f@Au^RL4+PGHWxE9GS7`^b_ok=_mDZ%vZf*I~iN- zQ{0Jy9xO|yPa`g$wQ;%9w-Hx>uG!0ekLP*MUu;d@M<4k6%TCxb9sOkXfBWeBq@SCP zPP(7x9|%SKj`oH6J)6e(AsFiK9Da0`hchai^mpuYw9%hUVcx>>p%#eFhJL^jHC78m zPSpbV9Ad58wSa9ysL%TpI=%#5_d>^~vG31h9JT@d{5*87u*JomPTVDY&t*(>jg4)G zmj1DWZT(^y3&ie&CPfSB;}@hmVp3BaQ!|nrwQ0$Y9o(sslauIJl>?my#5=}N)(Y?* z@31=tJNh_iM|@0?v00Q`&H6Ymzn4RUa)N0GjDr^01lDCIx(G4o?Q&?+(-tU%E-pe$ z1iHC~{%tHY*-roVA^qD1#vxOoO*Z33dqa2X|_LrQCZFxe*#H=iWBf{`4BE zq4TwXr9uk~f$wsJ=NPkPfqx9}O^26?RFsbQCH$@>e>QwKkGT2RU8`8TRYf^-7pFTe zTAb!Mb#Y(EWN)%#(#466yWy4D-W118mnS*)U7XHn?Cu|khY^j#ys|7)QI?t zXt5JI>{o&0jr3;~Dv&fZG%njeWZd=NT~@H^latQ5{)h6yO`o1N{`$`8V;JX*Wt^iO zVyvalzsT8hvR2i?xJ}MxJC}a%Q=SEk)86Ly={!4l=JA|_BQrcKN>?g8yw4%&;h}Mm zQsH3)3(&&Dp@+M=!owM@U0va!=9{jr@Nio%rNYDG1xkg7(lQodg@xFYl?o5z*vEWm z{v-N6jr-Z>7X2658*gnMdII~G%URpR+A#D^T~vS7pS$upzjUbnQLNE?k!K9&4n4-R zH|OjeAkVq1^_tF|?yp=PtbBDru=2W8HS*6}gO#tX30A)TZm_b|k~y%o?ftn{Yv#Zl z@<#rHH3Dn3ZA*wRj2Sdgsmy`dG1&tn2WZ=pu3-Jc@y)kEkS~*U1cPE&C&2Gny+;lF6VGue(HW`Cf8KmvdPeyxc^M_W^9MFD zpXZ`{#d?7H-mC|CdvHefgtQD*KRCm+^R$e@*P}D;Vx2?{bA+d`j-YU;Q(s4LbMB~t zrMZI!&N_Y6z%1g%oj!yidb1QG;iQy##|Q#r)(|MuG~7#cjeX+)>r(-H)ZQ=Xz>K| zvgORlPF7R4YU;|Zo59^ObcDXfV)g~m8Jpi3oG}PoFM>uYk9eWU>8A~RoHZB`S#zd9hCUEsp`Enm%6a3wHyVmK4r&*7Guz+>3hvx6r z*OkA@{&i`KgY@TSTa?lk@>}BF+Landd5SpT+Ol1A>)hR|G4Y79=}b7Nt0>3$2dIvx1c|*cNlQ1uO4itdvS0 zqv&HExAu*#W!#jr@Hod0tTnx?{r$OHlq#ObGt-h*T)8kxF5DjA4mG)K8`t=cgOane{W&jFY^_Jgw(}6S#!PFno_K=0~%Fw z@gBxyk@Po;z9y5gs~7)3RcpqUM*5wX7`Igopsyf)mXxC;UutpHSt@YXKUBa4%(dIN zdk^@1YgM3Xp$b%Ap#qGz>dEn>Sxp{@)9GsS&=i&PZl}NvzSUkt7V!y4Jowu1Z=(hYq%H&Sy;%_VF z?uriOgmaueVFyxIJH0%*<;7zTwDhJO1z{(mSys z_EwbTDZXcl+^Wc87r#Z8orc^xk=t^otksS^=ww}UWTY0j86JMg8nLn#S_`d&ZawKG z^cLF5_r2_KGrtQh>0Z+;>_3q*HQT6|_#L7jo|HSkLi(YRGb@h0-uNwDXSgUw z%BH`}mG+YI@1`6nQ_64SY$1`C(<0TVNY1v=;B%21FHbo;*#{r`1~6tgivVBw782GH zwh{iJ0&*^ol&iy!&XMvtuz6G|^?g2uKo<5m#T#|`P&h_xlnJ&5e3GIXfgf48Kz3iv> zhR{Z6CtOIn3tp6c%MWatgq|rxFCDTmXNyjfvm8VR`Stbcs##-i{RniEsf*&FJ#(CO z=J_dY7WVP>4&4QNCgYV2w{Q<7;}QRhs`DMjDfx`8-e%l#3*(x57}v~U{Nh253OHLO z`^sSD)P8E@&}V{`CoT$B4tg(GnRQ>V@|uCc%6jh6xtjOuc%Ob{u=0M^oL>jeu3Ql7 zh+v-d1?EexJv&&r3?JCEXM&Y~#QuFA`#*>MYwhT*+Zn^1y!ZXNy%+Xo{1oq4&KP1M z<2w&=_*op{r<}1USg95%M^i++;|9i8k1Vu1+WCDFw(~XceILeT<>;<27WQ|Dy&Z`! zr|2w)!*y0ahf?<9rU}WDU+kamsBralEFC3du~dg5ZWDQ$&}$aPSaP>x7XGJP;LH}E z9b=9d?DIy(g-S&g-w)hHmITIN35hv8%XvofWL;w74uXqNv@jMQV{CE`&vKp{35u|T z;3BMeE@Mm0dn$12KowZ>j0!A28@{|x1(sfkE?lGn!)YIBqtRBYt_Ph1xqB1ZwXrY0 zC+($8{P4$m&L=oSZ%-}IkM^8{PJE0#27jYXWxV_rZF&jq$ex-}BiSp^!d{zU{n$Ie z9_FF!890fvea7Jfi?*c17K8ID+T#;w>a^Hnr!S0iTnD{xh8Kz$-!=`6bDSAfxaCXI zUJsq)7C0P*Bl|jJJnm!sk~91`N5Z1MI&JTv-TJT&f^kjpzM*{`ThaZ^=>0WIv_z{l zI`*~U{T#o&p=8f0c+EvyM51e}d&fD>tHRGV?fSe`*O%;>h8&(qTe@gN^2V+s|9R7{ z%PS(yEB#e|5oyH7T#@(QmyRq4IOVMbe8tESy%BCwZPr zM?aD$dO(_^khCuHJ{P0%t+cU}rD*elY2G|3vo!@9O5*rkK>pS^bo4ZIM1rYHQxAIB zkC_>z1!ke=a(ZiltI?;`-*z2EXQ6)+Z6-2wKDtiYRMw-J@^k{y{OKDAPo7PHCw=gw@Tcz?!h0%kG35!rNIfT^Kc$`-=+6rDrxU(Vno1NMD)jNt zc0uSLgswqoCiDtI%OJE5(sqwgmhAJ9GDK&}o}v%2;RMEZo(Ir}AL7gTjKDqCxpKb4 zx7cs(gad@hdit2$59o6~BeWCV)zjE>zrgluCmbN0OS-NXM29}Mi7{~9SBx`F9V)aQ zhR%Ex`%bs#44rA}P(^#bhFxOn&@Z@u>CCCiO5}dhr{k9-o&L5jDezit$=i=EPdaUM zP09DYs*^TfQ=QcE`z0l_v=t>VVaf{5)jQ|%VCBc${a%dSI}Tg5U=8CH`m|T+FJ8GK zSm|WH?8De$JGTWZBeBga_&{Dfi*M-Brqq6p#z_481F(Ovkzb{+t6<)IJoB%ES%Y$G zYPzE~GR2|DAG6Tm*yrr;cqu#0@j^M-5VTZ;UdYk8z0}AU_%IIs=?za7&{tGPCOVFT4`UXlIb6N$ju>Y; zKBiR1x`;lG{P9VXSLF0{xR^t5+G2`_V#6}uR-BWQLZ49pEeaB2W=`JYx^eQJrtDwRol{H8mEd^HiM**`Wiw%@%g%a*M18%9#&o>bmW7qj)kM-OENwVMC$zDb|PqdA_qvNS(2baIahBm?GWm}+1 z5nQG0TRXZ&t>Da0!RY~l3meNt+qW9<{)IZFOetIH*$a)E7FZpb(0ove)!~BP)zH_H zVsS`)g^^aSBTqz6;Il*?T&gg!kl>nKx~Gufxz?-0;kmX7ARwn`WSF|fxY03 z2s(tf)SJ_*5 zmppIYceVY8`>wHn&-%_YTwpym3=Gkmo}8y=Qotw=kdF3 zkQVm^b5-v(Ty5`axW@i7aa~Iy-}o@CYUBPPz)9G?>}LD8WjEPZ`EQDU$T>6qHvi1{ zN1TQ6QJHh?*(tZ=)U3N8z9y?A-lrm)mk)TzeO|dIK0Bo{=atN|_#LGuG6xjVJfVzT z;Cz2h<{?w&=e#`N5$2Ov>%q6z%0u7WnFaABt5V6kFo$n5in1n=C!+b~GLN2D;#RHm z@_lj6s;mp*cQCgp-!CUmsO)CSF38zY(>$ZMb*^2$l^WmP(aRl^Q}mk?C+^I;Q7?C_ ztTh3q45hv*tfg$$hh(SB$*HM%WJVF|LE1b7+$YO!u~#gcWnaSmYHN;k>tdQ`j!RjZ_d5QeoMtg_N;-AxrgUYw$G}VY|qLFxbu&{%s#*3GJ84m zyR&jvlV-IMzt+C6Vy%(>Zu^{yyY1x}PrK(I-$0oScA@)q>t@EEmpKy}-4y={;d9BwnW{_?kQ^{1{HR&w1DRuO>mK$+XN#@xpf|&pnhG;r8to?|TS3eKLQUKC4M{VQoVE9fUXdt;4?___KgN3-}42^Zpv|GqQl65S3MF zuUQ}B4U->U$qLET1lslp_<46{6~tdk*=GB`4FBlxuMcZqz8{9)R%ySS_rDYVW&4um zN?`vT;cxsFy(GFw^pEJAg!}CA7u;u`cqh-@JogWIAKmeOoampF2D{1gZg}1;dPnq- z=$g_7`^0w@7ICLN&z!eu{P-os^FbecvVI3g;_J}RoW{j?~% zJwM87KZEzPcpuICaNdW}?+GuShwO+9y~a~yN8~2z{sFq&h|Gk`i^vIlM z)_UD1@GM$e=9X{M*Ot47@mytme~tKuNz-;SeKFq(YiG=G*UoTzYp3o;YnHyx?`03}|*29q`|7pO(?uT=3r>xuEZ=Mz7 zc8`mh@iO-^KEdx3`TY{Vg)ZOoegp5jco!Po!+RX>J9+=_MlYd}&_#4x%-yl^L+_5A zcrMRtd9L8u%ySpdgFItuove*@x{CT3sE&8xdyzugFz51rrw(p%0q3A%@8P)sF?wm8M@~KYOqhCDt>8t&1C5(YMC*FYf4zjNIff3d3| z{%z-^_#}Nwo@2kd;vBn8m$@SQ zw2C5oD1N!UqGGxIdG==S7`lRZ0&s1mzB7U4Ctx`pSe^owdBE}`usp-Jvw`JjV95iP zEx}z731w$x*QkVr&ko)L-DuTudTS%{z3l- z-1~;EBz~n1i%FL=fn{E!7Z{GF%h|v(x6uO(N7JQ#@QD-G3^{S)y}4fd+T*=;lRgsv z9`S!5{$1ij{awg@4E3p3U^8A}#D{F-kj)sfB?s0-Of+qeOP4L84HjV&yN3ONAo6jn zzQ3Pp=yMsNUj$D=zZjl`esMerrzbS@`DY@}6rQO((|I1pvp>%P4ShoV{O{QX8SGv7 zeE(^VbKd%CR@WGtK5o37ex~P`@w@e*G2_oXKd0{&84&)b&k4za@jR9+gv){OemZHx zd0&?Y;=*M>;zSO@dH)^%B6xiUJbz_;zf5<02k&!v-@m{-3x+L^wtBmcX`MC$GL=b!)exWBFG{Wxr}^JL8ZafXb$ zt2TZzziQ(kd=G>1xwtsX>63lY6F(zxUcy&w`YgSGGsiYo+}QCF;R~B;&gEIiSzFhz zzU2#-`s#Sru4T`BKW-dB5u7exr$$hOa}u(LO-L9t>@WUF@hL8E{G0ws6Z^Wn6Vn?D z;`=ogOnjH|{d@kI6Qvw{j-~atCB~=Uo)o`^AZ7mLw#11S-<~w_U4oQfpX3~tp6nd9 zhOjc%HB9R34ZXwbT*dpps`nGEAuhb`BF?^+x}`p;U+NSXq~4x&N_|p?l+o2o`??K( zYnNrw*M4l^Y|Hz7vLgw-2z}n~GmiIhypQ939Pf&E#k=BN@!n-w{B;}4y|NQYpGq2@ z<9KpfUmwM@%c6bllkdk7Pf&z^g`0`j|0+H@4%GFDv!Tx?*@U5l6VNNb=#lu7c^@h7 z+Si)A=QZ@Hfk)!L;9NVM2miJ15#Pqg8LjYLD}2|AKRV;EU+aJP``G*)*Mq-T{9op8 zsZZ+oPvLJ9pa0?S{~-OX{txJH%J|RJ-_N4I2}21dJR7FJc^@h7rv85RfAshN3--zX zmj3=P@VBY&KKT#fZxf$x{rx}u{oldiAFK?{Iwn zhrj=O*(WA{$M)dw-~M0bZ>dk}_)p<)6QBP-^7n)PhW~{99sfuCC&mAZ{*$|}PQT%} zYtnC6OE7&3;!o-6s|lRrxWV+-g!@?n<5O?woZWDq>2DApPVaxyj}l(Dly8vXZlh)wTAOMCp4UI+TxuTG@LKKl!re5ulZ8MU%3D2Lup;iBep5^ zy7HZ-?~l}fJM;A4F4o7n6nBYnhr~>aO3~kgq4(=~KQ#ZDO%ivAHFMu^cbS=1&d0yX zqUz;bcJrRGkErLU^hQ~d{*oT)Wxbl*QNp^r-BNz4QuUbv4|}5ufyoL?ggacp)Ve}n zY~A_@RUe+dS9p5rIh@|vx2`Z4Ten(I-+hNw3j3(!o+bUho~PN91x!JUYT+)SO^X*< z2v&lP5J89}L=mDldDz#Wub~XP=WG-(`ky+7y=bb%X;oh;m0y=nnQg4MTgQ8YHQ}p* zyz!ke_yO6o(VWaW;Gj~c&XPOJek$0*U4%2MSkHGhFgjVIx|y<+bI-lA7TvtZqAbl) zW-~HHixSqw-{6;Nt?u z5x^($g4YN&V#Q?OyPP!S9VPTRH2*n$O{w{tJzH-~fd44-OEbU4$eT=FGo7qwW9_(( ziw|7V!DSxrKY@oIe1xZzJ;Wn|LmG8T+p#wCSKChK%`mvY>rr2iHhbl%L#ut@m}Nyi zXro#BU0cb`tZzJ3aR-Krwt{wgTkQdsOz0r{Y&2)^-g?TSZJM3jA2Tc6(m6uPNU-dh zW$jhRUaOTO|JmJUNx+M#?yX=n>Il1f3n%(NBv3o|G`=_t(%6{o<7i~EC&JVv%Ua;p6Rbg#;CwM~3QOMWp zW?SfO3BGz;0{;Qhs9$KyH=jC`MmrzQYccXAlP|m-)5F^FesE(ilHQKPz)$(6bVyrE zdz$)0+VL^u*v6d0UUZ4YfA8+9d!PKe>Y~4XJvQR94p-kFzUKV1mGJpj2cVbDrZ$V5 z(^k7py(49MtFPFSO?x$3I!?31Xo1D>_tfeETU^}rV~OFc0rHkx2XshYPxX|J@Vwcy zdkyrIHRG4+?GU;juoAfB9A5=&(4|FSvkm2*TmwFVjWY^#*d`$77WU;=Q9pIgBz-9` z5BKo2uz%gWUulZKu36ak*}C!XW6`&yv7iTR+*dY+Z<6*q;IQy6>DUg={TQtqUnlK| zFxaL7n*vYy#vL%z+oBu#2^}N|U$`#k&Qs>kM6ZiJXcPS(JiS~0EABEmMq6+XA3CgC zr{BmPho0@Vzek#DNOP>RsP~AnuJ||8aZh_%5WNZw^1*+Ulua819t#{wQwyDcISh~N zM?(k5{ds)vlsw4>-D==H!GksFn?>K7Jemz3o4=)8Kf1}%mQQa?fXP75I!cHDP!K z7%t>nBr;)xmm^ZNyy|rA&O+z&_m*?dbCa?*Pwg6g+Tx$oOctt@)~J(DwJP78WB7g* zYfU-Qpi^)<;?BSRYBKv}nim&ZX0~zGpU19}GEU|`A@cvQg*#E018Ke*db~xt!v3mG`*Pu((k=qGz-HbfuiF~XA>$|8Oleyu zvTTj%pESj%X2Kt(=OXh{=o{MPsaf>BDN}-KW&!WQC(`dPI!mRL(RNdyLlv|Wy=C%H z6Ld7wRglj7Jxl++_3+)BVzYb$UF4ohPqb=jwWfrQA{{ z>xF=8w*}Zf=&R*r>=oxUQwo*NA{v+&?k8$4n7vT3B3;AdBt6S&px06-?PVt`#hV!wM8^5 zc=(7i58vY#+Bc-=3w`v1KKf}V{UG{jC3SrDPws6Soz5PWG`Ww=F};`Q4&Rj>^VF0M zf3fPE6})20y-ICdO{scZu$H@xGXM z`Q6UF(!ZDA#Qhq4g6|Xd^wa)er~iPbxTE>h#$xn{&4wOMX&y?O`PYxhZ|8TE{N^l7 z^W7@%){G9JYa6tXx&?OjX17Qgk5h)&DpF2gNjtGS?>F4_EoC*{C3j$_G+#^J#s|2k za-!@H$(Y_aOY8hGt>Iqu#=(B7-!3ckd2+qAZp-u5Iu7)!TX4UY5B|H?QD&R1fA0H~ zr^_$-Kj5A)iMxk5NhkG6T8aDX5$V$PbgPb$ZdX`3$@c+#SWTSh1CeK;{TO6FTvlof zS@FQ%jTZ@DtCdr1>R=fv-qOUCt zS_dTg=c~Y}@W>enYLw_qXG9_JVIN}q`>mIC`1$PzUZpPS@Qx-Sn@982R(6P73 z$VT?tHr^oZo29pb=u%lro^`k#OdTB3Sq)OQs;P`#E1$`nybYTp_woGjthypwMgRKl1AOk966jk+hO;FLfNH|F~mb zZ(r#DIA@P}z-2mk?2WeV65RI2+jhOpQ*@u)C-1~(ApEB`GJd6BF_t<7 z?80+~fv+B2*KWO}qmB2y@D+D8Hy0Xkw?V&U!t+B^=ieyzZGwM@*6A)#_4py~ zkUT;Q$)G$i9GCn_Mcs=857*W*zb<;ae<^QykLB%z$>(}==g@lW{UP( z_{5>rcT?AVo{h1{LcHn>@ZLzj=Z{C988+|j?lsm3cX9co#AE0{6bm8AFHg-^|GGP`#W=tS%IE% z`Ydl)f10wqkny1PC)@J%z*WfJ!A##}9V2{lpRsBY8VKLY(;Oc@@=y9!@HKrKPT%Dn zobd#_-F_wEi$-j;*;eZt>#$i5K_eSuGr1R0?ACG_mtyTG9>WTn5(f(NVVv#aT|GwHL-uwACmXZJ$ivTvhv1mjJ? z{Rh4L^Fdo-L<>e+nU$@RxzzPZm%VVwT+l;xryz1Ux~f3_^g zM>Yz+Diyj#u$NOWlRHe2pAz9W*Zy-ltEf+8V>5M%TpYFw9Nl(Pn^greRO)nmDY;Je z&t`Dc8FE#s#fcmyi~LYNXTrhjVpn;AW3*B_95?@LU0w|we$uy7&S=U}*c99wKJp1* z{=n8Z7rsmLL}~dxcuMRQf!71E5u*sS~b3t z`=3{f{F-u72t5m-XD0PXpIy*DVT8bMgU2I&id_Bz@Ge%CX+lSHydidkO+A_C=F%6+P0k&N8D;8E5LYLVq;@XR_5 zc&-7)x5K_aXMWet(!WpP`|Bob8_qfGXW0jT{wvt}>#%jpkBqUCS!?Shhz#KuJ}Bcn zh23y7^7DRk$d@GeJ%D`O&zP=>@y-VLef{x_kxx>c%YbLyNsNz=*NA8TaF#|ot@8o+ zKS&?I-O{=(vXQ=A>|OT9H^*4C&X@u8$(GO<#grLy%d=b%H1FSXa-DAUUoS)ZyC(@S+Espi9>g<*S z!O2AsoP-`$aGC;6LG;59Er(W{HmNBqQl|Oa8TK2U`KVH(yXTIUck8Qmowtwl?b2Ty zA)A-$Fm2rwX$_42raw;+5FBwoR*LK|HNZ|#=VKk04r>#z>jVKwZbF#7qSA8_Zd zNB^wDCTf6=(l<0g$9336>w&|E%_gv#c7p}q9yUg&H%4^|Z}8M@0rR`bClS;$h#=#0 z;g<##dC-Y25Fe+HIvJnmeh-~8s8f6$(tho>NS#+|sbAzy>VB59QY`#_iSNSOJ^7}b zv7G1vu|Gu?%Tw?rL1PQNc&zkN?y=%SG8Yw+Ip`s}%!izdJ`vCs_p*?{~!AZ^LHLeh>Q+J)Nku>N2?WtQdRx2{95yTWC1tWlmRj}KsX!S5qY zc*MUXy3GkbqTkxkQBqep?;O@IVf^IL@lgj|=q`NZNA*GA*79;E_C9u&7utsVMx2Ik zM0`qxj0rr95eD;=z5==Hrd>+Iy@JyeaA;#Jn}3?v2l%IG7m2@wcn@RQaC=njhZj4# zR$K5fTM4c(d6c-q6y$U2_|8)5^D{4SX#Uf>?Pk&D<(oX#$oU!4z83BD{3z-SGM*HD zv7T{CDrMk9O@fzpV?x)kBn)y$3^V2QO|n( zDj(yI7;X3^f=cB=i&4*s9;(RR(nde$rS39hP4GkBcFWToM~I&LCq78&6MrID@bBJi zlm5NiM@hfQS%s?kE#jXhTuS&oA&-y=Po`jdDdsn)Ku<4z4%)iTiyzrj0-dZG)7z8o z>8H|FqNfD9(3Zl>*s!}B=%3Ifoo(2)UVI+?ovKdCo3azXJ~&Oe*fR42o>_;wMw(@! z-$t6{x{`m_Ps&o+DpATa%fHnq7y54&d`$5%<{a+_x6*rq#W^W zj1-*GEi>Dp|FhQq2a(~@_27(6T2e*m1>PCgVVe-2F_pOoc&U*w%4YohlHQ5V-Hbl+ zTeUz&g_?;?UAjK(y9-~K_^^HrzICJ*o5o7JjDa?b&$G<*QI7?@=o56F9Rs~>ajJ7n zgqClMR3pc5ev!Nv@;)x8CatGmS)8qAUQ50y3oJ7&$)Tr@^-j>O#9QH*%-qc6b0RF0 zTrsxTsk}c+dghH{Yk1$mH`lvr68`qsq4?}jAZ&Xp^j^15ZFCzfXoILdQ5Pl?JxGq-nm4pCOjFUBQ!11=YPS)1?;Xk=mGBNubJ)38v5yeFS9QEO6SmT z?_f?yWHpBN5WP@zvdC=Y^p|OSk=b6z>@q`UKQv@^h$Z5|AZ-x?4-LIGd&|ej=YCss z^C!q?Og6quc>Ek-80*BBaDf(hR@tV@Ir^3hRN#E>04g(d!FbB`g2Uc8>n;nrz^|+i z3SVgK1G7+eV@2kcD9V>9n_cZTg;3MWfCoh(E_^hk6hB(v4GW)%6(X-~f8gn1b_3LbP>+mIb zkQIe)&On!Jf<~rn9GV}pxqU@P&UwzlZlCpY*XEw-W<}xaM*i_}iEjX3C*wMW9Ht zY=%-Na^R`PCpFELH#;+ezP@{Xj%_*ow~DdAPqusywnf=}nm&)A@)kyeaFsg zq2J6B9k2zNLg&bwv-C$^WJ~AMY&CN~@z@D^ne<0>_-#5f**97cA!k@+tz3d%+Z9xA zl#z$I#mQ61!}*J)oVoQf=OV3?z9Ex*R`LxBm49azdPL>{gtr8L>m6EW8NO%@UdoiZ z@HsR8o7;-L1bl(TTh&Z2beMvSzXGjekx9}8IPF`fJ$iAZ%+X0Z7clpZPC%DgbzLUE z8_|obxsfvUI2UqhIoV?Pzq);YGB+FIBa1#iTfaM^j`?4mhlGD_1qQFtwyZB%?Zd8< zwoR7#e(KSA1{?L4cuRP~i!SFHg4I6yf9M|=4Gu2o)ibZj-_oCoEhe-PUI)jz$$~5M zD>DAfP|5fhEX^_KL?37M`O%d&@zqDEy1S=k<}J4NJ$N@Vf_#;V{LEpzB6r;3|0=D%O?V)s zdC3Z7ob=4G>oVg*W`s}Ri``O|(3Z1HJHyKpKS(!CLSq?eqbzi%_$Y*Tth7-odRE$r zbG&y=QMQA{*e?<2$8|D)O&>NjD&e%z$Oyg+-3ICdobBi?;SHBBrOt}2`Wp1p@F6s2 zYWbt-@Ais4wgtO@r-nVIu<=LV!FXTBW4ZJ_!_Y(Yd7n_0*hdy%`iQj8S~Ctx-YoR` zROZeUx_zCp=6Z5fz#C`TwFErHZ_XTDOGAZAx9#RcpexYrJ^4{Vo9;RPknUVRtvqir zaNwILEfg6Czn5rB|6s<5d6tvt$21xs2<_5b@&i2 zIwXAX)Fx>!NvqQp9(cmWc*2>gpR+1@QN{u?ce4{Z$?s2i{-DQW_rV+Zq<5RV;iWz6 zY3n)DoOun_w1acPc;m0rX6Ff?32&6?yusKR-jMW*4c-uaD!2=8NLhu@O!R4ZdEvbA zLztgh#)PtFMeq?_%>9fdZRpU{H28@!nn||3VryBiKb&q~3lCgkO=97O~yhpFc zuywOzwES@V@l}@!?PM)WBrrAtZ7cdGPCGR@w78%$eFoJU%wynUb(2e0`EeF;n z>iMtG(u?mSLgNfhp$m4C*iSNEl=>sELB(b)0%o!8&_A8y44ZKsww;XgmSEcn+!|-i z`oYVGZRbVyWqc>N2yBAuVIMd;P56s`coZj~!Kb0Mb(Ep|ZcX2p1$bnvCf`T#REr`^K5h`gqvN_cDQ#wi3G_2Nu_S^6MZTO(uI z*ReC06^@r}KQ`VF;cw&$$eSD0y4qM`m4#M|@e?q%@ zYgFCh+cG-R%cJVvT$fsR=@84LDWe%jA#-guNAs6ap*5=2aWa4DXl{>WY(qO7V9wWL zWnEvaYFU6!GFek~_u4{r3GFJGKUfp3>K5R?lzDu!pE`1U`jS3A^%c!KVvXMt^3CB) z)pGKl4t?ic#ChG(=!e*XvMn>6eO2ikXrRBR@D6Vsi=Oa3YyUztba>zZ>|Y%=@l8-?Mi9C&o)j1bK55H88F&@+@|8%O z@WR#bg1{@~h)+Q;gS-~*)bWwmLq1fPp_y_zGN)`&vvR9#tBNr3XRGptH7>D4y_J4;FUD^fm3E;7=KGX zmljp$yx23>E9+4=hSsC%w542lN0-~ttDxPG5N}-rOtM}^@=Dn*Ew!$NvK4nv9FFs( z*8NfH!$v+F=S!_?CQcioMhR~7;8Ec%;X&%rdBQ^-mS}v;_*NuMByAvR+M^^+)(yc= zVeN7w_&0`qlXmN1Uf85h1>c1Ss#4Uba{2+`X^}rEXWeMkd7Q!D>qdho2Pb z&3lLFwVd&=y6i|gndQ7fn+TjzP8Dq_@o$Eea}MQXQor;W$Kr9}Z_z;ltKje=b3(da zHDK)x&87U-FkGJrD}NZ}hx7B6NJIXC!+8ukEA$I%0&`88J{VS(LvSYFU+EiwA+aC4 z>pRdjN_bGe`#@x@CDb2_ELq_10`!>7`!~YN;W~wRM_u2(lMvDyAE5uGY-!`$s8eua zt#Xnl4e&0`9Nxo|E`jI>Wi>WuX)R0X3&|euFEHj0M1^-}N zOdlaSvHjf;3;|$}`n)=S_0!?F-he^ct$=u`qr!-D>FGi|R~|;6J*=~rbnAi1O}l?Y znd`|fankmC>DNENM!V1`=YE5plYw38=Zr8t|3uz}Ps_pO5+hFdb2j)&y0eYAmB20d zAIAA)^!!QSir!I!dD345(Rb2cXb$EmgWdjtoFwAuFEr^_UJv}~Y#N5IZKL^|yFI)4rJHgJ|Y5A+V{C4Xi&>`Gu3*o8j8-s(CM_BMAY z?xg!8!qSiIgK{45iHkPVGQfOSmp= z-5-K!{TbNFW*ui7&8H6;b({`NhwH!(8D2-c!51bEzVlUAi}c}DUxn&eMfn1Qsr#yo zvP2ez*II+2@&sOiTloAb_FhPy{${=Cl2*!df}8ZMPR0eoCyi%gbH-u!8MKqWRp{t3 z`qvfh=pS^HhyGQ@--YNb8FNejYEza6eQjN(&mosq#@!3(Ul}8W@WdYAb_m@bu&_Q> z@HOFkBt9gYQjf$HU>B4bbo$b43*hyBd^Ef+^pU_Tc8lOT4|vN2R-1MDgUF-kE$Ls+ z3WGx%9)hF&gCpTsWxyfz3mgKE1{~osbv)%ryGWT*_7L8M?zSS}!+Td7~_G3$`NR{Gh3 zu)5@%)R$n;@jgRFma$$g9G@SEucB_jRq9{9+o<1vbp5r_A={_iuzgtf(CHT+5O#~$ zKY}m%4jahYT#gO2;-S#m-KNu!gAreqnhas!?aEd6y@t}$R~J4=l$01m-*r^u7gy6tEfgnrG`6;7*%q|U|K zUFGn;j5{xl#_zTiewUE$`3H6s{rhm?9Vh&+kfGnu z?jmRO$4A(Htjf@J?0)Lk*Kq5$Uw_^98wDPoQ%pOLIBdU@i4)teRjDs!p3H^qcf6z< z5wiXM*(*e|aDAmls8XFLjedA3^YcRIcEc{a4!C_iY>$Fhx=O1I+oOF~58H$Cgl10z zv-GoKbC~wW4My3mM%lliY{6S#mGOiE*0%oG%EoxY@)0(F4;#h%VYiL4jP-It7pd-H2O*FH+!_a)tIHGk$>yoSodIs|gM1 z_e3{pVz1P6+ld9lwHtQg;IKC6K=1g0|FE4x8~D=jQwJ?G6=Sv0w1Jm?Ls6c{w%CaU ze?<16*GbSy@D<*Zwvf0FehBemcssmj#F_0NIQ`9ty9S(exWnw^)~)nY)TghH<@aIO z7<>CuLT!~0#;bb0&}6$&=HDnYypMVo97PvMx*@cI@Lae&NxTn#flIeZ`(cyzZ+?uj z!u5yHUhtQ+jr!Q4fAdD8zEgYDx1Rc>4oMq(j5b$&LO6u#>)(8jQBF4HbeG?+d7`0z zwqYBJ{&D`btF%A5r#JEQjPagnM+i-8jB=!0DQB8d-V68@OqqL*-zIF&@>|A37VIra zZ`vF8v1U;G6gn*QRrm5PZG0hd3Lg74`)()lFZo0!yuw@XsPM>%MtSSPU}=!wq?7b( z_CXUn-43K&`7Sgt#}Z=<8c1JnF?d#ZxbgS+>%#07 z>4T?&*9Ceyb=U?u&#VWynJWm@a|~NB$EfEv($hA&9mYDKqi9)d&<>w&n&>li9sW<& z7>f;(N}N39o37*d&RpRU_LabSI%RC9&0W-E+Dbu#H{|;%Mji8s`&aEV6x}a0`_a$= z7aQfAY0zByX8A63X1(Pv()qymAvwR&NYDLhdfy?s{9*8sGTM!}=Z*J&@Gk9Y_9f5# z)J?mm_-*cm5&!0?GUsggJj3@9w%9l;gEGV)-+rsZxQls(RJzT zQ|o>xOs$)9p%yqD{+M^6;XgZ>K1Rk-*vn3Ii9W7ky@l>WL@vX}RWFI}kTIdG?J&nx zS)&xbd2}m!(2tEO^GJ6`8~P=odpzYge25;Y>lJ*6_$@tQ{$)SoQpx+C;d9uFe@Nbi zHg`f>>4#6mm#Ei;PH)9mC^}7i&HCIGb$Zw@=%q|a>lEFe4Xn(G%mG)uEW=m3i1K{+ zYMDpT)2HC8)zd>e3%)JkD=F8{*!~B)zRVb}i(E^b$lp~4%u;vePu*)3 z!u`F%yNBk7%F^*-ToLRCedudi@fC)2c5<`8@;m4vcopdOe6nuOpJ&t|eo8$*`7GZW z;|lVa;~+odV1erf>}OqX(vD)Yl^OGrQ<#@rL7U20sGK>-Y-21`C1WAxAbfeqS>(GM+=T9V_>e_@OuAd(m1u(|>&UAy77%~s zMtH^9!~f&=UBscE>MxjdqHppJ|j--R1^f(hAu|2jLxLjo5qV==`ky**x$r#@3BV`Zfc)>N+ zwD}ym%~vRKtYHe-h{R#!F#6N(w$G_W0dJf2Ve1jqUZFv#p#qKc1GLFU8R@s+gt-{iwn&knsbNJ zhkAs+E)K2b$Uqm%92xfj=6cyvCu@tG=s=lM8$HxAQ}*zQZVK-oWE^7d&3O)eQm94O z72x+MlfAwhUHW+jLeoahN+54Ze`qJ`Xo83EznT1~Yc8A-NV% zH$KBnz|nXTJ{aOXr{g0!8$HKb#})X~%lk2Rg&)g2%fS1#v!m>E6_EW(0&6&oYtbD( z`azjPmhaG5^0J?RIk%HkXBFQ*=36l)yxL9)_t5n7qFLlU@&fxzr)$$vRjs^$Q<(ME4CIYW1TX1$sD=pa8j9BQO52uq(CmVR!J^!RW?b8cbj`-Y{zPNxaFi}p6xJ08G?F74$P zUwVY$OP`9r@8poawIExM8D)rE30_Lt7<;ga`Jr_NJw7wWEG8|g$oIoH-MoB$4_Lp$ zwvcueT7Hhc5guKS+{n8N`1gn96&>Vd&6ldNOcFawEtr3o*jbDLzI16>*qdtQ%lLN0 z=9-GlHOR2J8V#GP8k;MKujeDmtu<^ep|d51`C;rW;d9o&#d@%>II}|MJsIoG248_u zU_*{|oW1xuJeO(#?Ibm-?47O_FFM16e2PEelfQM9PDMVmkWay*`fXQ7xgncECs|MJ zG3-QHzm}WD`f*_P0&^KKqh}Mx#MySqdbLbtd(fFl8vwJjB5MnM|Cb6}l`paZ>!K^O^T;t9aXOC(_LRN&S>DTJ=;g>qPuA^x#zDlWU zw86XKV~PAQ-o3qN{fyauwSF1*9ffN`Hx0*zaHWqvqRn4s4ESs12AS78s$AdEZIeN{ zt?0vQhJIm=7#!s zQ`c-_-cQE7LHN^LH{%VP2ld2J9%J9OF~ACK-^7P2eY$f@_t@7%f2GeQPU6Yfw}86C z?L>8bC~t=08@AjdA`*vHxuFLGRfZfb-NYv>zfKkr!9 zmk8a$`_ne|MmU|SvaO%^a+8~znaA@*F=j>|D(nd7lfcAUvJ23|AzWpRf|ie6bR>Mt zW(z8$|2?(Mz`(^MH~g1`^8PL+G!6>qjq?nA zWi0(%Ahf3b4Fm7FwDm3M`I`&1z#R1MDfC@E$5B4;nGHSX(&o3I^KULxqz&1M&{uDV z45b3n2D;pc--U5)Ib&z(e*~x1;3R#GhOg(aEk!@;p>GzrHW~0pn(8a}E zv6OGpWfF8zSx5V$TA4c#e1sma8e^w$dc5^jNZum^ALPe}9&151WNvl?y=TFQ+6 zdy4emf9NW`n*Mtna7(#D)9r6(c3frj;m;f8i3}WPw8aC4eT_{Uf?xbjvQ`7ywPOzm z9wyCyG}4CC{1KzfzoSzm-QSRLCG&y!tb{fquP2$bq09o-ICv;iZx7CBl=f((e=JY= z<@U(s`?1?J9`dY~+YuH`>)Vi1^)}dvnkwLMoX!k07Izi4M37=bP$A{Ri z&n+c4-;Qrk-=_3X9PdJ27iPaBBrdUfB4 ztSd@xJ|7sneLSH(Q4i$bR|r>u{sSE1XmEu=gjbqH*San(}m zUfsZ+jsdz)CxpKzwQeVI&{6z6p*UY^-Sfn0r-b@}yO^60d<93-<__Z96`x1woTU(~ z1A4&P4(-zoSXmRN`~9l$`;}8pBYmF0C^94CJdv?{)8}f8V=Uy4G4gLE{}S*N7){@# zCjngu-YUji+t^q4P1XZDdVQqO#gY=@mHh_{Ub*0CUJ*F1HO3awr^@>tV@+_{FVs8d zh^`7fkM`*SU)29enigX**fT#Y4th&8{rjG z&j_uyeHoIGPsksxW4AEJWAdw$wlw+GY)iqH`b*^;1NMjK${Beq%+}>i=00?}2=kA* z@Vm4kA7XQ~zI3F2%&)H+?A$`D6&F-23=Fet&!(pPBPH%lrL4@9n(LcB7r& zOYpxD`s-nwqj$vK-70T)?8GV0}xt&32$9O~ME;`zM!NPd0)y)@Chjdqs)m`da>969u ze7?BL+%Py_NJg|TUa%fbS4(Wex$|wZ1B!Xmxg$z?I*o; zZI)-M`Q4{48S^JtBc(d)z(I6UTlz2FDYosLM4iIF50~ti%xd87EH;zFqeQbba}EbdBpv4?e0wZ>JgaXiY#2KU#6gHpbXaZ)n^E;as*4xjLCj27c;?moon|%Mr?YDQCBUC%;`?pvVA$Z&9`}gH_*7m5J=Fx@M zHz}k4z4*!w=lA&Sc)f?;iiasr7X2ffg0n*delU7{?zjqg!Z=Ab7g-vwfyQ2F?4kNrQ{SbQ9>1_OtF`~N23Tt}9UZRs%fmD9 zNE7SPJUfr&VEZ<8Eg`?!F!Zg`7x3ThEsc%{;?$c;SwCQT?`6hI+Dor}gY3_*XeeT@ zA$t!M!(PwXJ|6MmqnIn50S;+mxSHQgFxM$t>gv!wMOTN)MyO{Eac=V0Rh<^Zpp*FU zRc8S=?-V|X&-cW=KMszXbLp@#VEJC+%N5LX3J1lR-JaBJX`)AGsCev$CH}ZUF=pnf z+v|VGnp*Y%#r6LP{u9To=D%;RiE$BkKPSalgf+5$3|M=d{!E*F`}ElBL_L)?;Lbk$ zS?r0P^+(S&1-5-^C#>Ny#2CNcN68e6Vpn7 zQS4q^+SAY`#n``w_;8$O2JOsY>_53UI4=P_dVUE$zHC_-erjRZPI~Nk z=7B#Nv{8W{t2W9H3)a@E4Xv$>9dFue@!v(;82)XHdJ_2XU#6e6uPxx60GC1;@b|Vs z+wwd)~8spiz)?)e$I5lU|#2(=oc{GO69!BlYY{ZVlNKXQfn~S)U|2oUn zqn!4L&LMw{eVAUsUi@$w?Zx-^h~ih>0Y8hrYmoORtvy>K89D~}w{!oWh34Xk6z#;I zM;UO&7~k~I?*w~)Kjr$Ke3Qs0e1uD${twG&%+4_!Exd?Xgkf7pUdeVB{aa>Z^=al! zjNKcS5It@EnnyB|h(Gh4SWo-rLpX<>SW6n8plXDj(;f+|_#tXvJmB=i0Q%E6amMiS z^`j%0M5~Dx-xgla28kZU7h}Cx*LyML_Ll>&NXz7>7 zmuNyh4CN>CXiUKxs*1$8?y1_p+66CaPSe2SW-S;Y%FIlRW91-`a4#`msJRV*% zk$@Lo#$LEJ6Pl}Y^I~&ZU+3n<+~h&#pq-WCi!*d3jNfx+(Zv%ukT^A(We)+u|87#^hR$VpKS{9ncC<;CO7;g z;IZqdTWyJ!qTAQ_FCFbsH#+)Y>F7D)E%h&Pl2-aRg-lAu;>bl6XK&oe+%j>$yK3$_ zc(3`;IDIU6lHC(+lRj;UPof_ambQvdineL(eM3*7TYnluSKsc_Hny=2-EsOzV!P0H zwmu~0T_Xn$iX3V*){}bg4VfASL4$=kdHM|AuCAAVH2 za1yL<<3kDVACflceEH#=!6BOjea&>{Bu_H)AEiIEH~1!GReHJ!o{8~Yb8bn^Prz5& zcQ3iu+_ak~7thAch`sK$5dWh?@$ESag1-8c9c!ei*P~wfB1!c2XyDyf-75Pv3(vmt zB&9dd_loT*zuH`5d0%bmyc%T4$D<2fs62{8yy#@ck3W?7W6_Sq*Jw@2Ob->mO#B%g-S}B_KlL;o!<#Wn<1BAJ>y_#n z$MR-O+Bnsl-+|5BKpfYD_D#^7vjr=)=l@0UacLfDRlg2kt-67?zy7!+w;0GKa=1t z{!F1a)7T!h*Mx15k6Zyy$bW4P`N*>EY3zdh*Vrq>W3WH+T^D0}7GQ&dwbCWI>9n2Z z93Ii^d5uRyaD9Sr$=~%V&T_+MooQ&s`Y?NzZl{9Bv(Wqe=X=+$0#?nZbq-}7p7_1+ zce=0ow}J0+H{V4w#vRRa<`W#zU*9gvrcN_A^ITyaZme1CP zT&9Tarbi{(S${OOnf9tFCta&E`7|!kxDmQlB=A#D;k+N6o12upW7A^zJ6oWs`8sEL z@t?DH>t^GZL>c#K%@kt{-zTeNY`S15J_zS*dE~7kFMdXy?yzWr-&4bV+tKdod4zi6 z)PubCr73@kvx%fHoK4Z%+TF_@^w;J#!5gu2136ECgXSEv;H>^m;(HnTX0URCcec)x z5>7GpuxYR95sZ%+pTVOuwP(}Ei}CCZ&L9(BS=K~|$G&3Mp5~9!*${!9IK=Mxijfw? zUy&2W&Hu71kOSpQ`Le$^9vp&!U&kB}{hL5`u^W@|J^KD@?TgN5xD&f1eu^jdwxa{e zcNdgroTT|^_HD=)xuIqxbL;Hq$G;kxu>1Mb_|MznP4IsGa>jJhpBi^Z@Ij{l+s(ih z7mr+x9N}|~<(wATuyM#nmbhO2&ECmnH*j_-X~0iIzhVNde(YK7c3t{7c9_R z6n1+O_=;BY_2nPj2+fknt@=DTTN+qs@7vN-^takAvE!+(5FCwsPvD0sPfUB5n8)<{ zpEQ5I$=cf?wrt!V69zbZ-Qv)327#3Z|B%=Yh1Zd`!npf+w8ZdP&sEKldeBP z<)+$l-?imBLghYgzkQ2u@QSKsmdE0RFWRfwDWN^c9EAPPj4t8DsfN0R*cq_z4d+HHK<(#+4DR|fvWB-gfCkgL9 z%y;3T{2PQTH0r_^eu(r4Z9Ku-o$cr1%~}`j;%%aPR`VR#P>mhr|0jsERo%)Pdd0%= zFMk|3I(L|z$4YZfQVJUQHiEHWn)1%>y7T|5eo4KIVHAr~zpY?iGzpAaI}>A!C;ZdI zoYjA3cz^6EFMF=FGm80O$Ku2vlCks z`*;`KlD6}U3GBW4LGwp(=xeN8=)4P+9c6i2{iAtCr9HGWz`xq}eS$eg&h+qU61RCo zgDQ(h8h(OKeQQFDmAQA(IT}Ot;h7kdISV{l59N$IPxb9b_?9i4H^!#7xp`xL z_t6u3khVBIWNlS=t-)2c?QZOk+FD}g2;a2-7uf!p4L?nzUFBI%J+A+L&hIRJr1YQh z+sTXKXG#;?-?eaWAx%8FzqJU*!6zzrKJAMBSzFIq+DM>>{QXW=9wiTMztdS3k27q3 z`3{2R$NU!^#k-;-u^JyP)pz24>YHrqQ#{&}U4;j|7xA0W6N1n6iTLw;i=X)LOXN4S zm!z#MG*r3tlf8A{3)y#X1Y=ar+SFJ==dEWM2iSWk`sM^=vt-l#xt2ND2=r{@ae>W| zZTblG$#b8mi;drH=XqnKts%xO7&X?4fQRHu{g;w$IE%V8o;{7auvan7pCWVYr(wM6 z*6>B_on*mWis4-NB>L7P-e8C~D4%Sr)0LtvYe)vRBOigW*)Hf&4$XXAuqnd9v%Y}R ze#3Xwtyl;;{IEasUpo02-n*Bvgp#vKhlpOxdAj{dI`{D16O4Zbb(?U4$MMs+`x@KN zIg?s%^9nv*47)TNyA;7L#j#5XyT(^B^iF)dIJ}u+j_-ErXhnzTkrSOSJ&Z?ZgtmdF z;WzUK{(17(YJ5(=Mw!z}7MYem5+5{XQp+UH?lhb&8a`L_QT!Og8PlE2DJHOaS=x|K z8lw(p8~XOco**40oAj_he!qiu)qj`JmiBkuh`xN6JlfADo_712dN|u(`P%4b)v5B| z`crS|TFRF&pDNux1sNGrXWHAu!zY2`@z`4IdZ*s_G6ueqFOw(!8TMrkVxCIANlf!s zPXxZn%MYXDH5R*zI4ks1KNU?K#C8Jn4K79u%!1KFx7Ax7(OUGE*ei7jJb54eoE1NT zE407ySnAaIq}tQp%C|Q8A&+?T)5K5YBcx9Ze4@)X$<@hi`kw)@rQ+~HDMP-UCt?v+h)o1W`H8G4k zu>0aG)%|5)lx`pf?)Qbyqm#e^o=_j1=jadJQ|RS3%6jlr8|#WiGmkUG?qF@Jcvp2c zSiey7gRc-5sJ8N_dG;DR#&YY5JK5VCURSL4#19(ZX031Z222G@={>y%==bK_R8>zn&I(nQDaPDe+#&Xsf*!8U)%n@`J z`Z1(AKjt|bXH~oFumL^Lvd`|K|I7?{1==O+d1D8v@&BC<(}ldLT$f!JoAUjE@qK-| z5;)bT)geFO`!>zt6zALZ79X8)A$+n0{!D`SO&?4r!Pg6cg}%$fi@y8|M)*CE7|8FB zoI6Y4Xax3hz^JpNZ-8vfUdq_OYBpX2=*a7k`N1Nk1B zlgm#-UM*dn3{5YfZ5J?{VrlyfwtGA-la_nnMw^_#eQ={RDhYTJ!R4CjcJ4mby( z)L--34oqo$M`O``2>?Qv*ke{W*-qnYRs(#ta|1^+2w6h-{NPTpm>?OY+laoKLgkY=;4#dp9L0RkLeBU>2lt{o+m~UrhB2!vo9N; zSQNBB0oZjeRP5q@*pn9ouy@u4uvdS6Anb|rg0i#sqWgqgspZRWz=K09--=(|I`2dI z9`JH7ZdAN&IBDYDpR&Hw_d)QL%rfvpH+0-TaJ+JcjZ1}P^dBz=^w(N=zr1wHeL*?N zswe*o+I5_S?UMZm9{FmD0mT*fAr2(FqdoW)v=_zB&nEU0#h-7$_cm8gyzN5#?F4mh zp=>Swb{BI4kGuAeY47oYGL4_&^I!U(n2SQ6Nv51!xBYh@XTP%JGiNhIr-#8se7i8D zFBF#v>kHQA`TUlCV4%M69ti6FNngGF`r_x7M%v@x(@1)VInZ#uCDa?{yU#OMUWKh; zZqwHb8C!4ApW}Vs?l$TTmpLDO;p(r}Icp(3_Z~6auuLxA5!4^I>#eU0)h~HCL^=zd ztG-cx?S$S+|Joq$3G7j6uco*y*QauZf5I_*L2OT}8q7@%H;8_WN?a3;%A` zTx(o5`(Exr0C(9j^}qUF{&IzlB|3lELvQx%2+IE1uBm^Kb+@YPk8b_8`sJxXXi4mH zU$hh*A7h+97(aYobd2=#Lu?rO1e}HM$`DV)&aiq6{Sc13lng@8N5CO0OU;kn{$PKF^_7FcE?%ae2Jo_jJy?H_zQot>`uRYZ)el>VH;Jw~aPv=A`AZbNMFT zHNb;T?~A+U{kh%Ixqkz1*;IVvCpx3!{qr_oxuwtV57O}a|45qk`-3$6{!Y@eV*|hc zE@I-Mwfe@{j<}tl`53ZN=o3!2@W}@a#sIg{5`2v4XR(x)_JMG2|KWg!oo{1I#74PSYzGtd}P zbXFXusWGrGpM!3KGv$wK0@$M#_G&*ZusM5P3i8ECbNKX-CR%ZiN9k`FKMEhmCmPR; zA26N?_~aV!B(^cxV?3ihTGufikYGFUoVF9ot=Jc|~b?;t}F+_EPvd zY)58r9j3h9b`42bk39??zWuj#4u>X=w!efsN6d0QQyhu_xnCHMoZ0SuZKG)i{E|yi!YnCJh|P*V#Ir@PcizNz~^}j z=W~2lAL(ud*>&mkMHc2FXc^XhwZ48C7^nVsORqV6s}AXmv#z$Xo!q;xY72Bma%7@? z88VAqnOWnX4;`Fw7YXEUa)SKgOShl%vMX)fY3gR3Z%TJ%TpQe#AsnyAca_cS+7jrE zP2i(*&=mGYI^rfL2UQ}8T6F@fv=dnFcTB116 z4eN-V?@Kov3oQ$E!zZkK^y`LkzPz45`as<+2H(%hu#ueJQNHN;Q9vvL#Y?Fqi#euFYT z-`TzqJrygv26}dCF4&#$%{3zej;lX@Q;#`sM+f*01L#MdWj>rv*h06 zx6YMRy4Kj9%x~Fi-yWia+Zy(^?avU0{GFdKh=64s4&seK!KL%P0Tn)AZ3d?Kn_l36*(?yw^H>ZQ4Is9l91dP@X2*mJceL z59Sx%UJyt1uy+eAy`JY@75S8|uCKmNU71i_sz>c73-Fj=dF@&2$2(s5BYhL5*?F`j zovu3WXC9qGf4rN&zkJr@U-$Y&!hh zGZ4NLLVbD@@QKHL8q-&bH445g@To6@ejS%P($e)K&{f|f@LL$(+i7n|2rl^?%6F)R z%f$m!-*8*kKY&Yl{#Gy!%@2b6@wR`K^R0teyX^O6KeW2~hP~}TxIg}8b(z|_a3A>Q zM-9Zct25}gGr?CflrM-;8>`=sf)3*KK$k-=^!vP(#B%%edl2)zl(c^RPFi|QCPi9y zWT4+aF$kYH-|s)+w?h5iZsC*d75_+{gx}qyhxL0U^isNfgx?aM5ikB78Gk>WejM|l zDwlnuK*s3;1Xp244;EwVW)T3Qg=d>@GJByB3(?A^BQC8ot(Y(yS~7X~^O?m5)1M z)&HLj&5JoZ=VbBMc6{RGP5ruUdmtm8FN?B)!C2-A(!;WN4e+ae@ylDpv?Ny_MizgC z4itV)7JG@Wsg9Q{kF@!+S?Z68d^({M|)V^C69rU&CI2)b2z~azh{f6IL8J%fumP_A8 zy0PP+$0fHRKkE$gI-lWZ%*Ci*3vF^IFvwqs!*lYr7*pOgjkZ-#gYHFjlYro&W0pe1RTFTAN&H`9<(c4`?3tTQ~NN zx%%`#lhT4QSDzlZT4`1fe0q?6aC+b|JC1U+65XD8v0o2J2dIz4ACHqRS#UJ$g7%M+ zrhZ#&`%OCGHDWyK_YX7X+KAqiY-r4-w*Em3Np)irSzI6u+DPaMofw=D|BKZ@~G^IPS%0gvpS(*;UXzg%Z&B3EbfoI;FL%m1YzWNk2bvCia>b)7Ew6DVFI|i}M zR}QevL2T>s48Ek!n+VzF%Vt>HJaXh-u~lRB&~M(^8^4YT{rIK$-KYDB{Wv|C4B_V1 zW|r9glWq|W9<_aTC^)JAUHTT%UHkX*d-t-R`)lS3*UP<0JW1;Y-{M~P%@O7+PpWQ5 zUZ*6qAK)Z!{_%`aV#rRMEM*tA623(inz=H7JV zqhW4*dP8|pWf|Wu;D5e@xgrbGV(w>O%(rIt=lL`nhd=!}&*x9ozlOShL3>x;lIUr? z#qo3)krh{-YV0xX}M{?{Y=wy*1cZcJCs?% zzCxAR+-x%2Mj5|M1NomD8nLEN$3%wex1`!Vl4*Q=Wb z4P^KJCt6xD6h1{KYClJNoIF+J(Y^B?K7I##Sb7iPF1k;6b$|TkvZvn}Y7Xm&3~7ms zNUSR#Vb(=PRP*-MJuvs^?w8?p_07SZD|}iu693doo!zC+q&1bHFxQU z)*SCm*-y_~CxKe%d<2w#9m) z|C6@1?5C}{wk_5f9B5ng*$oc2Z8Z}z;~FRIn~t{6w4Ufd`4O9VgXANgo{(9B&6d4! z@^U0H^igD_FHh%$%vTHYl#pjqI8Wm6%q0bR4knMz+w7|+e|YAL$`jHV5%TnXkB!UJ z71Y65nX9xmqCd|O8Tn6PcwZ-vM;>RRw{801IzIVd2d?9#qbFVNVaJMc<>=A~y0jdf zR)e0cMb9=$7c;-8HAbDUDn8C0F!rC9qMNlpQRVepI=&74CS4}|>hziPdl=7q!Be`e z^Dfr2TV1wfW3TVSbP$VEUe)K@dCu@jJW1Q%VSV|-rmhLQsr>~Bers=OHs;l-eeLmF z@{2^zQhqP|1$)3~{}$?b){LB*r%W0>nrF_vY@C@NtL*n1WQ%VCo@Duq%Cgtc@zm#h zl$h7=pEO(lM7$**MfzL)zrvO629_iH%XaR@pOasTZFv{h!m zEuS!*4D;_S@Ms_{rhdc+t>R+cl)gC>c$`uk-e?TQw< z>rp<6_%4u7;u5hjnT_9Lj0u1Ij<@2OiM8)9kFAkX-)HV@3*_d>;xxOrvo zt3zX7J8&K9^tEsI*6ydR#escDvaVdTeC`13-T>Sp;CAYM+H*Gj`v;C&SnvFRHPT`J zxS9WV5LX(2|HRn7X?;b2e}4J@5&mmpnQ>e8)1JjYkNo=dJ^+7h1NQM=>uYRyDVBL> zLVPd$HsBv>e65mmjfLTYbLh#g>*A9XKrqw6$YDZCSr2&zj%^ ze8OArq3f!C$=RW-_tm(>TgJGavsu9Luo&mg{D3mQ8){~jQ07+j z*21Y?yYo#HC*sVbRrS%5d4EB7I$f9kOuv3wYW4V1`lOV-f63Z@JuZJ<{`E@QYK@GY z7w62uH;A!_-({QBj_%5K^~Rp@*M|DGCCT~lPKP!k$5TgOYq7D;wk)=`MZ9n!-?ESQ zmt6u(DxdW zv-!|L><0EHOPzO7W}u!a&5Zka>Arc<%2^Eh9;loNzb3|d=f4od!LkeNSr70>=UVo2 z*|DDRUmoF{AL2EvA6m7L_X^;>m3|Y<91!7;3syE9nc3ESWTuq$Uxnj2`aH9U{-@u4 zTa&kN-+Tb_I)F|!&}sJj(M*{ z4!nJhabomEStsWjF()COuZ50DXkNscj@IvZ^V4@3?t5hX10TdbV(Q)|-na@Hb#Vvw zD*9zJWzsJ8&)OgJVn2@vC-IW{EFL>5ljrw1>h5IT=~~K~-)cSCxSZ-*q5sb#cfjOm zQbYg$PIqfk&gUUuXksqd0HflhRgB3z`yW23EMwf-#QdoKUITL^W!{q<7aJ6Gq?Nw@k zPV`-6J#?w&zv$vYmon&b3v?D;MN7$83$Qu*s7?83j!sJ>rZR<&7XEQ#T2kPyJHNy) zuCErc4*D9}52t;dv~)q*JkplgG$*T zp?YHMt8?{)eXuVU)N>|fE+owmulLVIf?m37(m&gC4r|fb1Aoo zyNa5+3yu%Q&$p0XXm<;Jn$BZ`@$-TBG{pXAs(qa$8m8@d;6AmcUk9pFli~`Qyn^2b4xVSfIryA{6}QOxb7rApx{hMuP}F% zJfmM9yh_<5<&tHb!>w2udApfs49olu^88(C)S))u zTd@rBPQo+9@CKjz>x*MBHj%A8kT{0Q<-ZW(sgU2G^%jEH@!>DP!SQpNF`?tba%3+& zFSrO9)4a5^!NOO*uy9r^OmQ3G-b}1dc!U38HSCi--Qxe{0{kbw5B}oY1I7Qt=pmiM zqIQxO_v0S+vzA$UIo)w7^wN5ZFbz{78g??)b+N=OG<3dIBW-BhW@d3$0K72=ouP4C zE9n}yJ;%57=ZNK5A7=@Eli<>~d#DGV>FXQ8QaGkd0K>V!CEWF0`qAmc2CEOl_TaAt zGI|o_sl#9Y5%?Y@xu0>D&0_4l#?!Z4(>rB4-_!gaJC3zRJ<~Vb`toqDa#LG_yl|Q>-}W~pUOxMFQ8l}aU@qR z98nh-|26|Kx3y5O0eN$m~(Qv*WA$Gp8<~tV>0#Y;^FYZ%6`3}K5e!A zHqG*C(;#|d6}%85hU@rPF{CoeNvDhjmM|Z?bsm?0NIy<|e976>?U$}e^laj+XvNg> z_-OTvKa;G7Ut45m=DCY#Ht=m38EqMY-q%@XUQu;7=jH559>#dhM0e?2&E#jR+cz=C zRI$#~wOxJnO`50YOsK;yIooT`8?R;aW$|tkGnpN%{kY^}+Wb;=`*t&A-hcdr{N#Ix z=MKuLjktc(uI~6r{}|fk7{9Xy=rVlB6=q1wwMC|<$QX2GQEoQyE@xgXb+O0Uzow^} zHl#x}7q)3z5LaT%wP7fIny0U2^WvSvh$yGE>*;G*_j|hG9wqz;#udr$f)nGEY+JuS zp|RY*!7G7}pgj>sWv-xH=Rdh|B59oKcsXhL>w|kbs?f$-v zh4}aJw{=!*zrWi~8Q+(Y4`h8AH}95Sfv$kR#jDwntfWJ8XRdwmxyCtns6Nfx;j1T` z`pYMm^urULd&>cjYr}+mT@!-YwXvBtzDxb0zs5yn+j>`xf+t^&4$UnGC-u!@_`m1;JdPUC&G6f zPwuG`y_%m4%4=;|BQYU=eGzbId@Xw_{hl;c+Y|$$zD$y_#n)`?SMUgjF9G{k!MCvO zmmcqLyXo=%w&VO(+nX=$-Jo(egTLmX@4;8!M4WLs@^o>Cex2xOC(mvkP4fubBV%?Q z^w^%dd}Pel^~49^!`Clo%~xsvfP_Otb4(}OspLi`znK|&GcrEZXqJ3r3p)%DfWBD_W&1X$><@|#7 zj-|92@jSX&xTsIA48S87B3p1?0q zzb`3hQ}ar$P4#<<*sl6~K)bdq=UA3vub%LAadNC5!@T<0Acm>5YT`F>?5xhi=_b7| zZ+2|v3Y$;5ZPAD5+b`4R2knF3eTcr@LSFfF{p$e7_OAm(=N7I5e3iAyzO7{4h}O)+ zTKD+t0X_7y zuT#+2UysCEB&|u(S|qJW(t2O5M_Tj3o((Tx&jhzYJ&9dG6e{!Oo9N!VK%Xk7Fk$(8b zKHAhcQf<1jVcin5`r*q3ZE9TV+Vph`W7kX9sp!(>m`=F)b5NbzwX@6Xyoy{wT0bbkiImBam z^ie!NHnW+2?pndv;-NhQ<{BPk4}Bjmc8<-I!i#4W&_r`)`=iN63-oN*7CHLWAPWu9 z)A8rKfB)dLn6wXC2(Eq6TzX5qJQUc%{p)-q%@HP{FC@S7S6;^^hs!CB<7nW@ zg!wpW`S|zyu;rS|++V-NY`;BN(5_-cuHC`-aX$S#7(c4qzG#yI7SX1GvSAz@ZG>wf zZ5&;kynec%Y*=0gqmR=O(%BPq=ydYke6OPa^j~>2?%mFP$x}jU zBZ({ae=p5F$9Ls(X~lf+ShjM0L@(ANFIGVXMN>c3h1-1d^*c1+z0#0r?V(NNWO^$`4rO$*EyKZND>p0&S)-x zd2DE$BA?R9vXf!0c?rYh_($c$Kdww7WMh0?Ok1p*8I*4oXA<8!KQwG-9Piar*2MtQ z%;m>Kb7J5|?Gl&L`h&lQ^5plC=h*`I!*mMw<$dH+yu$Ip0{8%)x=a7#>9WjY@ac*- zf_S#4m;nAj+ZDb3T3Y#URq)ixQR&q;P)`xGP%P-j*z=2cmoZn=Ig%I;>$BU6P2K+t zXZ?ZrkiAg};#vA_*psAucMc=pX!!GA_S8k#!!nHhD_bJNT4v)DZ5|tG88VFX*^3hE zl0{}+qNtj;w{G*?W4q5N;vNq6$;=o*KKNO24Oiz*&NJ9V-T906)cxe@Wls+|nAjI} zUP_%w>dI4BlDhKLwS~GGsH>5>=1|ue)D?#BTGke;uH;3;4u;NHxA~CM*X4%}UH5Rr z%RJcZWi}r&Y~3Z)`_M6_=i!stCp9bBSCyw86N2e07woCK2HJE-hxqHW9o;9qf$SDG_e_3=waujm4@}RpA8{J#;|?`5i=f>zks?2T zXT(fi%Kkg~=E(kJ`J*H0Pqpj8Q<*36(X0CVf0;2Wm1kHe&$Z+U^T-wC(VkaV2lp2Y z_&%5K{@q5ljim+O8~FY#Et*--V|hf_{tne=F(# z%6lpAxxB;h5YG(h$+5Zb^8dNldrQAeoH9m!jhmn5e^{QKJTxL(h5BtG^?%U3aq=oA zbRB0I>O5uLV^mL!u$Z^xxDFp$F~bz`sSjFb%*wptR&NnE)taE0&>|M_Qe|15@e=iE z9Z%}|J#~HZ+IdvwMQD91xTwd(tJI(xehzD!`XTYna*s3vocLZqdDSaPzm-SySlL{j z8TZrjz1HApeqFKkR@Sv?o+U9UnAg1J0M_vJ+qKdPow0O_ZCmp(+nV=YU)(ph&-x$D z-K1F)qdvQN?&1%x}x!y7|k&+H(D1%KPgJ+6wO*DXmz8{nJ@ttNu|s&6P>B*2~Fz0(o^dUgxeU z&u079eJ7G5$6qTW<(X~egUeBP?QM+nB}KcN4?vCvv{yrWbKk$cEa##gXnX!#NtQKf z=o&|#*b)8o3G?=vKA)wu;vCM7(A;Aqcy@BPhvV5g#%1Dn@w#Bj!k3QL;`K7p^zD4n z9sBP1MD!Cc%g(5su4t*hHuMv;OsNepDG8zYeP~O)XpuxsCl0W&1gN-*j z`If}b98cM>O%Wc>uBlw1Pph+R!eeytxXbVI`f?B&%O(oSxcq~ym6%}jeo)*G{}6Tm zIFwiP(jDxAML3I2f?wnC1UlXyizD+5&`7bL>nR(i)BBA(qvRi~Pxd?R4D?>*o6J>* z+mfzyG;@7f4U9%M9@yNxgYFs#_l+wv7@c<(v>`q2^83CF{aP3kN^0B-J+86se9%7P zzRnK}m+mC{pg250z3xut0cTN6)V)oSoYw8>EDG+@#NPm&S)Z9D9*Ixmx!y zPf6kvXCr1x19B?=g>|e`JjN7N_|bnxk1QT#{BdvUkVMa|%!f8H7aBv><=5a#`(u+V z;{@H+sd4!I?8&>8JPRV+DfM3OhOoZxnaH|jdx!FDzPmCt%%9Kj%u1CTaJI$1@-sL4 zEqvhp<-djau;6LM7gsy-X%6E~j9d8Mgszi5d&?U+EpK&N8XGPCZ$_^*qr0Tr6st;r zoAg=}-)}*$-NHA`TRL5+a(T%R<3z8a(Fiyj#tD8c8cPF(QTV2O|u)|;X zxWB^L#QVdi+3LjQ{=7~}t`*tUIJz0RjY}SQbS_6TGNG}7@~eL37alHu6ZvDxD_pIf zYNE`;1!Y`*)vx@XU%&C|*FAHxqd(vgEw?p{%~Y-$n<)m*xTTf$S*(Iy+au$5t%5EF zo)OKT!;b1K5}ivBhB4e;AwI9tUJ5=HKGg5e116NuUVFTE)lbM5_Q~Bn zqi-Sy^2c>AW-aG)Xq~jj*+RxJPs2Vg-EkNA<|P$TWTb+#A)Nmsd+33ad^qR-2oL#G zhB0|edWdfgjJMo6#x?Nvn!bG@B^3+kTUTF_GOoTh%A}x?_J24zasHRF<6g;G9yyx> zom*+Qlk@AFEIbB1m1O;%Vj6;JEPSYYOC3zYOKYzjZWD{o2`laWkM_oOQ zmZzn&4Q1mhPZ@XK%>L-*>X6-0J-UBTb}g*`*K&Vg4RC0k`bOGUnGdSR(>A|od5I5q zFpn;ptF0_;$#w|l2>qhAG@mY8Dm%q}s~=dm{~LJ=`S_i~IS+)s&^Xia^K8~9>HI_V z@swtN9xs}!iIrqLct^3NxXw^HE7;p#eO7AvY|e*HT^nzSnX+x-{UkgpS!mkRJ9&w( zFH3wsO?&GVCsm(+In?LLkk97!bxRJ^ziUGLelKS$JATho#__x6tHkdv1}h$!NE_la z?gd)4gm(M%|KYL~5BTRI`F<^H%oE6gtJlD@u3phw;~XcK@ZX@k8Rmr=%LiJguKK;D zM~$k&ckWwMg|lE5&4i2SC0ZQ;4i3JwwHxB!K(6}X>*9=g$FGtB z$M>G-^FFfk(wn^-{{2JpZrg{<|MZ8<-x}iA8Sq`7KLXFT5@Qk1=COlL$Gv0w_M?9x zwhph39mzUh-f!cli1vyzI9nq6x%h%xuov#imZ-#Hrlb+7OuL1|A)$#9GlxP+siEc=I&L|rDf+m5HT&0tG{tmiuE7d zx88F!_Z_mfGtY8lGcVJ1~nkcf)PEnEO0O;pbxe?;UF<=RcOH>>Y0A zH!}~ngs06roiw9Rk%Vq`L#nV!3M znI6_dXEtr;-t`@k%%KEF7I8udwEAA?VE<_@p?9m=3N?TziTUJ zBb3(QQ;o^39aht>`bE1yK$lClHx0BZwxg6Zz@M5yew zz}0X>VtP1DV+hqFJJn#kbNs%9Z-&5AI>Ruqvtx2i2XUVEqX7<2LKEeCmbrY%Zx&qe z1u8Eky_|L@s$Fn#d%YfkxBYfQX@0xlM{J|+amu+m&bM`=KW1&48qn^IKlHAuC4Kj@ z`v#tyh;5MnL0o@*4f~YVa+Y7UNspqBo=O?he@3EjD<1Ow0Q73^?kM}8dV61c8+{YM ztAhOkqtQXp+$P4on~=Fp+*`cq3#R>PY;l&kp6nMA?eM}=hTnHHo*X^ItiM@j08;LY z=%fyOmonCeH=uL7NQ>YPi$06^raXV=_x1dqOqte*L0-_g?)=3NY)ghGdUlEywDC>l zkC^_kFZqPm?qvZUuKwrHO|MX9>h9k4`u!(PH(L}qN+fm!P&lmdo zbW-{~{NKH7lTTX%ExVv8cRa0+tE9;Q#JW{xi04{;q+SH6}XFUvEB5 z|6P9lHhJ&KcPS(I|G~RDVs4QBiYGsriM?0hZGS}nBb1R} zG=%$`Zy%SqaYby)YVLYIti}}OF6phBR&E$Ohw>GX?;7&m(U)&XZVvex-wS-$73QR= z%#q%>obL@Ll5^$5wy6Xelr7TOueHXTuf4f%!5?kV^SmP%tG)4!8@&~Y?h5?ArbEoA zs*_kdOy8%mSw8Ie$?#w39+gP0ZHLLQe`{3{Wjd)_|M8Jlwc+C*X4AY$iKU0Q-!l>$ z4|cyNrI*T=Q8{l$dgBoH8yF(|F1;FkPoFgpKVnpU!pvU~xn*_SjBcqj)2GIve_Moj z4rhIe=bS%WBRYHOS=H%JW)kU7R>n9-AXdyXf@dU83C}p5BY0vwlX#BfIg#fip2r-Wx5 z&k;N^o=H5%@tnwW63=9w={%?LBzR`_rVD@`gRW`=(<>~Wkc+AUIvzOwvd&++QkxJ40zGb^tMXok|e6Dg; zE4=x-3;i>c`32u6M$BvF_#3mKaS`Rc-=?}}U)C`F0qTjENJ}F58R9S6tL^2)7izCs zYtlpVj%sJpad)Dz4VdbuR^#D4#oO5}V>@|(mVjMCL9sHMWU>}n2i_|Buout+0Iq!jd(znw6;rDa=9|>(l zYp0KQFT2;*^WrDXE%(eTTKDdPA)E#5ty{)kq4$92ALyCCqwg9=Mmbj?{nWCxpUm8| z!^>=lMl$k=+9yWv!;aY1!2EFa+UlO#?^pLU98=qqdat@g`=r)PWL%9uP=XztSTtc& zBQQ&jk|)>p#7h!4#*58usbiu&hk5Y4iOg@~o6;4B(|+eqNES@greFPZD&nvbSP4c5qh2xY4!5A52N_ z^6^i~Cs&-)_hlYUKUE74B}YYj?%@0EQMJB)iIBdQbt9SsI+gEhq4QGsrJMh^^}#eI zHxgLdz&$k~O5QQK5e_!el4GMiih~Dq8KHACC44SnZah@kH%dm`xx@L70N zv?obE)o~ta!9J*x+#BR=gSP18*Y7+C{sRBmqou>8tU%*KFN>UqKu1 zYVWqJ?VFt=0{Rh$Yq=2GCHu;c$+0#)gx~nwi~JYd;xSj3p)TQieqXuqxo2#9?;xX& zZyqE~G}>anIlfVR$>sT_pJ#mTfpDGzcrPPzDn3afD`DCfqvM22SJd0;=uiC5_j{hdQvP*G z?hnjkX>8`~g*(&y-36!P?>y@Jt0f-|Sp)PT{{Q!`+P$jj zd(}OyIj^VnMvt>~%!Vxaq%YFgG=psb_a|e-0Grl~$UHgRtdxB=#b$%bnU6NlGUwiR zMY-XO7xsz9u{H4`;;htR()V3~-JBQacZzY>O5bL42QWNuj=@I4Kg%~4WyCW@F~%Ll zKQu8Ns?T65X17e0=4? ze=yeihu5AaEjfH>dvZkreG%PiMzG&7f(%ZL_WT&yv<+oH;3$(x|EQX?roFAwvCkbN zeNvh`44Oi-Ovf?I-ylm>D;cN5e;pOXbF!Wv1Cd|-9I=tqKSz5y6z5@&>YvFYds2nn z?qbcN^mW@|!M&61*d@s?XVv>WojN(%(}taTkN>{@ho>4o7VX(gUG+trV?$knAx?er zxm5N~t}Hmf3!_pGjjxp5>x$6kPGSYnP5HFP@b~21h+gpf?lI*X*P`@Op^NNd*BkZI zt62wFb&|!0c2uAIj;gaf-=+zd)aLR2TJSMS$Ci88evOHU3Fj_m46_E^+8LctvE~%w z*}j|vwoGZVXU_;`oWF%PS*qfJ-{!V$C|M%OCI`sW$&J-f7hmH z0KZg-2KNlWy|iL2WpyT}%E^YOkN5IPg|Epx$Pp@gA^X6A=Q#Sk{v6;Tj=Feyz*~}g(88FGaE1fvYBTsYA@_*=mH_5o^pzQt z+gO0tm%uB>_ch=il#Td4vCCJz#hWSo>&PeHxLWZU<{1BtGFOh~t{nPN@rd#KW|6{7 z`Ep8YlFqGV4icJZpEdSt*DLg!#%k$h$?orxuZp`3K0}(%2QMVMueE9a0Z%OFfAb5e z?&UAUdB?i1(YJ%m{NK=b<*b*!hVRSyzT$<3?rX@if;`J#DDPfIzV<@`SR^;iFI?H3 zKwgT)Q^*PNxjOM;6YE8*-)nv>_FhfPT*}mcKbY$ie`LjP%ZZi3@14=|Tm#?LM?Nm> zwJ$pYxrEO<&mO4b63Fd^frol;iVawT9O`pS2>1Km*46Se{q)k-)kEFypR}&lxWG?;u61<}V=DrO!T({ctKY-Uq%D6hi<%bI zU5Ve}^A0?p9+_X7TpMd?0!PITmPONk-E)`hURBkLF2ILLzfjpt{=;5ImhQ%vtL*Ld z%R5~y`V92x9=a1h7Ad*e4vPZ_?1#^~Bt5ho@uy>H5D z8RL}Xp5`1x^}p6=N3ne#eexB?HadGoboEX-106Ibw}84!OKW>dO)33to>+3dN38U; z%s;TziyJ=TpUIWQ2Fk}dHDaDP4I7i-UT7np#Xht5sICnjE40nh5uNx~V`)n|F1tD2 zVu*nl`bF)YPrIq*pUgDPO-+|ibsaJ^l>W&6F4Zj_P`=&CaLR*K+3yZ3Mmt^8x6`1pZ2XF?8|F4O5`M*?5K@W6L&Q zRT(3W^`GGAuj3;2)z#bkgt@`X#ABysmQkj2Pw$j96FqdW_Lgx+A$d1dMSEhjoxMrp zRs2hMC0iTC&*Kh8;uVX5Mf2K@?-i@?BN zS}PXpTWO^p`Ggu9{f>6i{8yd3mu>dPLcP(w=_EPP_(gTEk9wI^!0Bl5b7;}c861MM zOuXZdf2y}?4omYMZ%0}u+{*fqYbt7cE@7N>4}5k7G+2WS2;YuK*~vTLp_eJ2HC5ZB zZ=qjZ8N8QeTqOT7pc7}tfRlJ6K{-bUx4!|ngMRkmzu%{a;MVzG<3=RlkBIM=`D+I( zjqageKaW3pu^(sDoZQ$P`-oSSaoz{KQ9&QqVMC@gj>WcEc;lm2PuKzNUIykrB8#1o zs%>fTQy$)R4}#|%;3-*BeLn!^KLfKXUrl?iyu(**Wob(|C%{?wMFPAl!gz|WwZ5?* z*W;~z`&D26uis)aT0@||QlE*R)z>;(r4zc}Y zr<~m`-PMWgXE~F);i(Z?pO)Fc9Xe6$Uac{ejp#V^Qe7kYns`Uk_1zNLok-Z{)$_v*Cam%nsqWZhq@ z4!->F=SA1Oggm#^o|=(ds9)ZhJp^7DijEkz?(W)AnI3dcz++>49dHk4JAYSl3asiw z`G?|(PUc&|d2$k(D#p~`*Fiq+vRkD)Ok;dke>28pJSJYTyp0T|Z9gZWht95Rg%)b( zTx3zQBOPSktKKRf6`$Y#HrIzm@x#%%akL>BX+lQyU-<0d%*chr6|!Gu98SDIvdO&N zsyBz3p4-2i@cTr2|6V$@23nm3O={P@^6^hy{?->}K&!Jae`ogT(CVDad*+=1P0n1` zIZXQC*LS`~JfwH_q!#FHb?8xjbQfJk_w|v~29? z+!FPJ#r?o>=ImUb*Y=HbA+I^ygu7%<_!jcom-=`u!1wQqup!LtX8Ux;Xx84kxj^ZV z(YdYsSDz=~or&ZR`n@EV9t8L4A>7s1>Pz*t=%Bt<->a|H_v&}o=L7qDZ`{?_!dEip zp;N;$CVa&YVO&KE(E#1Bp#+%JZxfIi@k<->CEADwCKUAje}bRu^Y4c6{^P=aylV!* z`yb@}zv^>0&qvJ5KmS~@BFS_D`{d?I&__=S$KA{B>C?SCj~#mX`sESy&@kvLc_>D< zqR{)G%e59lav;3hmYu>7!$^EaPo?5K3f{2$N*Vf&NBmk7R<=JME@PJB_>8s2rT zi~)DulPbLwwi`Q-Jp(vu=(96{qZWA1LdItR&!;XA{G-RPC4pWgKJ)*{T>3<4TFt|} z&#umitrzQUR<=%%tcCsb1;mGXSaad~9?%jWBi94{be7>e(V~h4+*{EYDV>KCUAKte zoA^B!y^#9mH*QiqtC{%DTFy1ckL}jl{8$v<0)KHWXPPfqbMu5ntoK@M^ES<0)ZILH zakuisyzZ$@{3cdZktV%q?v(EI+^OBktIEzB&D!uC(0PDOo~Rg6u#>bsm;&o5+&*R3Y)_NN3MFsYv;#bH_H*IN-7F=gO_xLF- zjghi>zme{nyO28wM^{L1s7xEWlCi|B&bgC`&Fh}saXFP)S#F;EDfPTDD9jJq*!QM& z{#qP6&)7-*W?+o(iFoJUF}?)t$-hnWmXEf3fj^h%^PkoZI609lh3k*uBN^r~%hKpG z+9D26SC+ungRh5EPg$O^lb`1K?>|v*3|h)(jiGnM-v*leU)BHL?_2*b`{esT*M20~ z$6(AqaW>N6Yd_+?e)MAo{@RaY`Tu{h_TyG?>-vyuKf*jbI+3_iK9j~s(p?KGYM-`# ze}#BkHvL*)m7aeIKT-N?E#m`hcm;Q%z=+_W7V zjA!btF3?;0O8UU<<8f;oBvU2CB{gP~-W!5FF!+}Z#O|9I&(<>+a1i{|%$ml|GR~XC zmu|yfD<_uIz#Z1iD`%3-EtC8A)to>|d1)s~ zUf-|co8Aj5OlJLBllcYzQxWC@h~N8p@sIr&isJUbqgb^3YWaZ)V)tg888r_Yh@UkN z(81lfsvmjH$RCt$aqDn)FKf3xu4v8t5&a=LnB!`nmVYnbRQl1`k$32iF{~SyNc|g^ zdl|(v+t)&O+W#aEaJHw0naQc4yoYKXhkt%zuE85Qbs_UID>uitvIcEeCpe47ZPY{G zR;Zmr_^tXjV2_gQK}yMIVg8|w7(|Sb|(Z!@lnG$Zv|$%)6Pbmy%!efUF^S-OU3! z{r#6!trLczdxjC)`w!?>dt9P@Ht#mZ{zW4bJwr36)nznu}<)8>N}oi68f#)J80Kh_@&+}+SS0j$`tQ9 zmNt(b0iB6I&Ze#y?c_^L&v87o(Q_fRo#YkeW=t^akLR22rLFSHc0EK)q$_$*PHR3U zooIUIP`1f9mE^G+z)3peed6|fS=~2>1&Ni&pzxLf6i)G zbV%-wPWTBN{}??u_pkVw^!KbAf%l`Vi^0y^HI#LSFQfmOXsZL7dEh7iV>!PkK|jNP z!~bE$rsv=htYI2ybe~U8G2iZB&14q1-vY+Y%aYTD3+wsn)(=(uUu$6e@k|BdIP?MY zZ^#dPSFyI%%Pb&m{qRJk_`8*Ps|52B*+bAhuR;&vyw-oOSaYoDIoFj(7y9LAdzsm! zN#3*gDxIv&?ik9tm|b4#9BPsWX@$&O$?on4mt^3@^ja36xf9u~!#abF z)SaY!o__uau*g0;+UmYk^-II3e!2=KtuYZ@6>Er5zhbV2d0pva$T zuHgLCy=%S9bl$QHUa^_H2)i&w_5og=lAp456Sg4f>+vzUqkva)z&M%}Df*4_H%^9T zhx_D3wAX~)j~#L3sIty7KQ@#qJMxsK8gr%M$8l^|l^H)TPaSFOk1et)q)9mny^ZobUcENLY0sAAnVBqN{_(gGJbjPk{wG|0s zQ)(kQz*daO<>_8H*8~vWmnLJTCc!`Z5i{+1a4Cr;+E;$_oTnESv1T<=Jar5N%%`T-`DBMp&7|HOj@!MU9$n1()ie;o~gF1=pIAIvEpV_nJJNt&Lzw7 zi{O>mxnoWdKbqsrsARcmuVZb*&Ft0K@WwZuR&1gf{g6c`rH7gIz~RTJzM-@;u_>iN z+C7w?bZDgIbmmA_U`J+@ne~&PWgMCgSvH9=Df1f>nfFIOcL0Y+yo$Nho;9pn(0VA1 zk!71=*dM{yf&CTknlHGNSnd7f+d#gB!xBC01z5ikURVu$$>@X@#+6UDA2P1x2+E_M zH<)Ni3vE5gxGwX{t76@pfv}-XyhB;lpB-v?v|i#eA1{16V)vebrqEFRVvIRCiHy?! zJbn z;`2)uk-iX6LjnbMuDq&r`cbqAv~zs}^Mzout<#M`xlJ`>G^ zi{Ojm_ise@bvDyGimA}|Npw=#5l3DD57k`)yqbGk1ASOyx@!${OVuXY630Hqpiy>d zvU@1sis-lPk)gY`Q2!R}=k`c+SF|KJk4Izbm*8pfm2|*T>Xhv%rQY%2c0Y8e2F6bU zFL>t0K$F{{4^C-`Xz)GWcVkC)M2m6@pifuy2*t%Sw`2cGd^(M<5bntk?!o$=!*gG- zxZe()g#Q}gdyBSvIXiteYsQ#!C=JtNIcu$#Ko9LTd7EzuuXyT0;BBHmJ6;d&WK$gB zW$HED4?x@?(?dS-fp|{e{=he6L+1chs{A6p-3yLusG|n^>v&VLwgFqAy&Mg;yw+UZ zMcPLCXd}ACh(F-z1aSZDYpfro&Q3FOn(L!Z(xKB0?~ICOWS{-t&1L==j5U$3z4QO5 z`x3aSiue7)q9Uo`R-{(P1x00dLvjHDg+e7k#Vxqpdx5K$d#`sva49M)D=SPZDoaW$ z+AXcHEUB!ltf(xlthB5wtxQEg(DQ%ZnK^f3QK|3e_xT?^JolX0-mx=Pd z339|n(3{q9HiDL$Ae&T6^`k3%`+O{8y}CE*A2MeH*}TaY_q}sjCuvP@JlCSG)}!yz zx(@XNl6gpf1{vg1f3zP7eLDnwyc3@LBTs&vq|b>yl-5rOJ9Z$RktWkeA>FP&!>%9w zw*N)(%tjVtaYGM;RcZPY=1c~xL0pD+&G@Z{9iwXT!Qi7 z5cUPWKDps5_+feK34R5Cw0AVua)##<@6^4dL?7yNdr<~;z0wDHJ8_ZJ06vcVVSd}O z^o!w0hj;*${RHSi>yku&^*7fOzWFp{+k4e{+C<3hVAy4_XTn;1=?uA48an`d`6iT6 zW;zVtX9h_qW53H|u>Y({7TP?|aY|AG;KM5$}9a&H*%MKsnc; zoLfaXsXXL&s3@a%`Mz;0AC*llS1sc9E*Gs8se6EFTqJp|6!Su&3&{lQkbja_ut%e; z6nUWfs226{G~#2wa`66pq)+;~o~Tnol}2j*4#Isc z=45py?D;_6GjR?Et5fG*BfiI&WG_|UPUXH4>ucB#_5kVkcAC$?jqfcxv#iRg3%L(; zA)PA8U!+$h*`fkzi~*g9jzp&ef==1U2mS7O7-a^Z%0a)s(obSz2Jc_N&wP#Xh~$Cl z&eAJ>C=1c87WryGxDmg_H*BGE5b*9G`l0+A^{bxGd?>>7KZ0>5vC|qK#61wS%0>J% zrrZo3CRbp*$J~?gQtC6H>pILUNp7aT(+jkli9V|4bsh4$htk5>TPyLe=P( zIT*iaoY6=@qiOuWrnv`5#^m#z-ou{6x&gH>8Z#iHuzBd`cvk@#_7bEgqhD-E^jTA} z40{a0LnY~A=<9kuQw5oo^05QDa++7|0FT;%xz`h@w+iT{w_XJqgXkvmgYwE3pc&<@VIZHc&>W?X?ZMr}<|wZ{(!!-5CGGY7QE~m-=KGi1^Zh<9mmifi zaCyVyjk_!T?%k$qmh@=jJ*Qp&9&L1gmL!+UJrJ_*IqmN1?#6$&?b=a#u}4KvSin-h zOFGv`ouyBEH2A%!oZr9e{ND8);2SMHrkvkDNm8T-qz?YCDd+d^CO<#FWxngBid%GvY#H%Phx{v&**`1SDnQko`}`c8u?Z#o#u$)Lb)8@ovt%?sMs<*SSR zJ9g__91zpxr%n$m4=YyxulaTXoHlFdE>R79FEC6y|3e4Y^j0wIq5( zVrpT2a&8{(N_2>256XZypz9`bQIZSfVZ(;Wvo%(e z!IV$GLu?^7xD#>W;_|dcvrQj5G)_*oTFq8jYu4%I5S?t*+Z|SuUMK4uRtlGAFzRLU zi-!n=mL!we6iztLAZQR8;s|s7w}ge*Yy}#tUN>}sO!*iWVw(^bN53ew$r?nclkH|% zV>Ftz8oORD)EAnqC30wpZK#}wtQZP4`FcFMVjW5;YY{1>8)9_Gxg~ZzD-1nHMCZxZ z7|i5G7ki%S^*qJx*%o0hw1AOMO=tcyllc#jM@w-o$z`vlNajnDB8xOu^*6b2`q}k` z7BjW4Y>UQT;8`+JE^=T6T4FWWiI&bVjHjIg)@WvUpWkDSJvtQ_KqVE;pGVW^lw z3nN*GO%8=ud8oX^fQan{dPEKyE@9PV)a2XZ0x8C;9Y#53lpGTkJ(|TI9UV6^IxY&s z6Tf76f;`=#w`y3PGD>X7R-DW)OD5F5{}B+WD#iYX?F zCoz4dtT*Wl8k0$Hv#GJjtVD7$b8rSsGuZ7$Jr6faZ_P!MvX{t(Xm=jrywfjIy?SLs zN;I2nW<*H&$go=tTDwhN!dtsDCoPo6#=K^$K8GN$yl~S*3v!rr`aA==fE&GLQuCK* zWkyFup}E>@XrL^O7UFl<^w~vrcyz_=qI;6TW-)3?WP6Flt<;QO-sRSDudY&0Gt1(g zD?Xkt)TU`_x{Hb}#z*-w!(*YvD`Ru?#1$k-Ej7`T$D2NFiY&OOh`g2ZT1@$_NL}Ht zPEYoVpq20oEqYf3uJDHTeeZqT$0`akLU13o)2(M^Z`{eXYOGr;hyv9AER^l;gEShHd834u>6%%C}z_Z8V zO(zy(&XZG#W%A;(nsUaAPD}Jq{Y|lIECmLwP4(KEzafQsoz`p`qP8h-f0-)xlZnZx zzTD`7bUnu*uoN}~fpmhA5XWma?4oS+zS;1K_ZK$aXlyn^zKQs9s;Nk0H0WghMo*(W zM!Cffz0FRf<71Vq4rX$m)m$jsEPAaW&wzodmBHv=Np1l-Ur>R)||h|Djf zSNt#Jvq)Zf!keT%F}k|M5LFH!oB*Zo( z%#B*a&1H{J>Uf#w4CfR8=na=IC>lPhu?XlfLd_pqn!TiX22Gkmuaq1ns&Q1A(QL{` z;q#1U4biGuyRittB+X#6bKwM)54yTH7XAkEqDND4lBjvB2n(?cl{I$RLch=nAjTt{A>-YSP!Sd3orO*a`!kXR%F>y4~+ z+aNPIj5;}2kH|GT-l8cwo*^flgh17dK3wipkhthx5LaK(6r<{gglJKDxI!nV zrX>#>+MNDv#&6RU>9c7(IG-wK1irBRd&IA@8EUdcW+P?*BpigvW}+z*unbmk37gTN z)uVkHjYLz50er!&>2kV?dU49tULDG-SG3SB&rKJMv*#wuh0&u>Q=DCBU>qQsdYY3F z@w^Yqx5QZZ&N$u<9XDv-(EIfIMr2IN4T}M@Ni~ zh>7`6=u@Dv$reMg-Y8?J!oWnWYhFlxSV;bYFxg@?=W24nY)NR8%@_f|*_JeKfbFj* z*N9{=fq7xHTng5XO3b#{twUr;%nmJPh3EnW;Mk~PHsetFzuHj#>+}t#JoANQp#AA5 zo4_zlc0J~-vct5Kt+8v2vYyS2Q5z70L60^GaAE1Yx(s7J2>n} zS?y_IY@W=9JU;j%ZNX1z^33!UsvM`zrdu00ouefUonjOvx_QUf=Df4u+q{FRr_tC}&={dZhelF~!8fg~9@0;40MFn4GNy)-6`v1BQ0MV_N_ zb14$~BU?m)4w@z)Bwk6%HJeGT=IVF00y7EWqWeig+>~@?2e|aV&EHIu&0&FN$qrt` zhXlsW$_sNxW^ho%Y9j z-gQpZCk;%UHY0Uv!j$Cjkpq_nW+kOg9gn^-N8nt6vjj@gtgP`N`GK?Mu)lNZFN;RX zoRgkB90?IGwjq8s z%U^T-4Quoy-<`X(Ezt2C@y#^x*_WBk{MmX8`m_z>vkr3vRYGK{Fw7o|aFUl|xA9v4jM8(XM#$h5PoP$hetsS3bKtl`R zyG^>xm@>(3o|BlHD0EsPAygjCR5UW&`P7V!B`wmW&G;dN=(067t-)|!L_z{qvHlo> zPB8Dfzy*{hT_KK;VOsY;{IwJ^Y~FQI;bSB8$(S)~hKH*GE{qPI`BaI|GA(rJXg{$iEOaNHI#1Rw+aU)qdgzSj zF^;PB6r$x75`uEFszaslI*DMF(vEV9^wru(N}Jk({NIjv{|9w9rDUUC>jIQ46ko1NVMeS&}nE$jAHPc^UP}RVO!3yO&41UxCPoF%Qi8{`CEz2$RJ_*y3 zrq4*0l2Q|=(yRINxx;+106t)TfA(vBj(E?_znaS#ZH~`ObmU8<+4^G4@1QDbr3}cv zYc-VgLvr{MIb4epL7s8h#6b%GB7T(-v?TX6V>uzV zY*0EXDmubqp&o)9c71vjMkAiR9X?)ziDNv(_D1PMRGXS zTOD$E39r-%k*a|mtnCIz|6BT9Xl3#O@MTp=Elo4}p1Ymr3-b?d?{a}v3L6mE$3>t; z^9!^RCJ1u)Vh0wDuy_cPu}(mLRC@on`MgllWx#T9ra_zMD{#1qPzvPBYsYDV#8jOZ>Kb=&a^Sn=C(|*@^VeM+0fFCbe-jD zji3?oWV5vpW*%aGXo4!RNROo~r_w%5CiZJGW08vwCMrv|!H-s}*X#5;KJv9Ae^{v3 z#+={i)B^uKcY7y0y7P^`J^e{vd>*Z)9dr@#y6dC?V7Wl9N_>LxRT9rw zhk-p8qT&2QD~2Bif}ys9KF#vgTKjHInxF_&KFV_^Nns*CE3Rs9KR>^w7Q#kq7)0wL z@KchYlVAzD-nTdX3)9PBY*YU;c@^fEqw{6h%^JC!ZZGDRdF`m5^U^c4Ki<~*g;1FA zNy%KNa@Jw8!mb?pP0y*cR9|dLLk6T|EfeA>R{ygBz_n;QEE0yWOjQi+?z{!^czGGb%s?3} zhhOrFt5|yq6Ys)=OFQU|Dd4&LRQuy?tsS+?T&_L!CCcZX53ly*%*ut^bCJ;@)d-MP z%)&fAab76q&+VWa>|p*U^&#T*vb&&FCwKFj5p9~+NMxi#1+@snVGN?I+$03h4|PB? z84T=wp^5i2O!C=td+5=se!y*RoxhYtFJWDQ{lL-*Pv0z_ZCszBGZe9XD1}1*Q5enL z3u6S16u6M{i9*Xr9?v52Uet4;xyV2@ZWeSf=ksycoX`C&=4@TEayBm&nYn^~Ng;PH z#@w1Brdcu}m^_VyL7xpseeUBvX$Y9kbvKM5>#!Hj0IYTFa!ue3xM9Ay>Cx>`FeKS<52tKO`mo? z_16CQTdSwG3E2Mh5~rt4pD{Hd>-?&URS&BnRz2~Gh+V1m1oEo z=`Gy-VfzsC{-5O6HSr(@c5XE$%Q~NV@KUskZvRs6<4|Qt zK8*QOY1|IFwUOToC7?_HC5P*1?HpRVkx@~_xTz81YDfNB(?hk*z<6?!MaTIh+MSJ0 zZ#hL>&zy4qVZJVuX#YY##>V9fvNz>crqEL}tJ8MXhg*88>O6s)ypD+&C1b5?TI%G? zRCVn3oYu9c{#!_IsorW1XH%gTtOeSkkMpMEWOcg%Rt9NzE~EfhXG2j)8*{KyNafWOBp?8;ol1_1M0C z{O{JiG$!NH=F48{x|iCV+fxrM4)}_@@EW$S0Wt7~kD6IEGUY?bu z+W){FCWH?Hh*zF9rzQ zZYj6t3@>&D2&uWHDB393Btw2=8nZ+c=Q=>xVf$BE%R?xfpmxe$K=z{aY!feJrh-xzF=#$I5O$NSGf}zQwI7@{QRlkp`Ah(s z(c#?VWlQV9l(5_UQPk&uwjS(RE|>J-d1wK(mv)rLNiSEtFp4tgn`kKkbxSME=oy|I zqaEq|57q-1U7UJ&Zs$2^YPwfmsZF&%zvt7FhlCEJ^96f*Q17pVGHb-ta8-;AHvobvPbD)+esHcSqY78cp5vm#m z`k(M9dP8lWt`_t`5JVUlwLT_9C2l!S^c&m55QjZf>es_ zjn~yUoAXej*ZIcVUVB8;M>wrdZ5bpF@v!eZUpg?8 z&d{MU&eT(NBQQR4Zjc=XBOLb0&>viQo{^j=N$d}QW1%3xugcM<04^3IOx!PXBxP2vvkfWcb&U#i2l?p;^G*8 zHRkzK_JI=duSh4e-`fZ*Z4&@^D zF52) zQLN=IjAM@|(IKR39YWcL;tB1Kt?R;`%VA;`U6ebXFBpaz(9Mto_z{+}=ar9{Gt&6T z#FU=ioTQ~}NS0i_aILk*u=KT7zJ8DO6!u&wo=tkLk}+F3ViwEXEq0Dp4S#PF?#%2R zKHAr=T=exG4gPI>u0GMSB<-mu2_4D`i9fux;rvh5da#c|)LMbvZn4EhMymcCiP-8c zUp*~VykL)RnrMojoEB<|G?CcKogX>fVV|Bim7Q|RBtagKxf{%pZb!p1hYK%%b~F3F z|Is|GSvsy-JfB`758*UbEeSatL~-P{>dQqltw`AQpCwK1Xd7b9(oxf>;`MTI_C3|PiZJLza0PXwQ zn{?y!wfg7sH2J*?J}4+Qgf9Gg;mwO8jo%n=DjaI6F*(eaS+-jiEIzyV5$ibHH#Ud8 zyW?3$QqlKC_9cPE6~!YdW#la{-!kgfS8t8It?IV1tEyIwx&4*fN8GXbj_^C5zH`v(4XgXy zweGI2ci(#V*}IGG`STuK*>`2vu6cjWRrkJl@1V60tPNbZV%^{CH1~aRU*i2+?;rHQ z+6Vl~t>p*Gr#-m+!J!Y`|B&y)riZ_MIC=f%^?e_?<&nQ1ne*s7j}G6kc0=O^ZN`Ph9)t)+aB0s^qEfo*GyANafke?5C@r zmY*qo=Er9yKKtmiP0!AIZrgL0KJR$`%jctBxa);KUYPvi<1e0laqi|>+vab3 zd0VH~7rg%R>z&@1|HkGwI&7b_{rT-aZ)Uz(`DWvr)82aQt&?v}-ci2e$d2*VcUK>% zj(Pj$xA(jq@=nn^@4s{DyZP_F_HL)0vv)qZ^VH7d_tw4l^LsJxm%jhm``C3TX3sZ! zbzjxKv-^_1x&52ZzUlSt+;2C0d+6Jt z-;Lw|GDhvFMjTMXy&2&5A8h^{L7qQ9{T0GU;6zz|JO%; z{r=a>f4lCtM}Pa_w<```cleRR-ygpG_xZoC|NXn)`}{HIkMckE{&C5XnMc+g`Qk|T zqtlPxeRTKHz&}&|y!FqWfBOCv|JU_@z42GWU!(uF|NYY6fBilDSi!L;j{S7(%75nn zQ~u9a|8ze-?f4zX-#_kmV&aKqCtf>o{6xgbg(sgpdGO@rwOO@mYd@T%|pGb_(*KT~@q;%wg84QKbB4N_8+8y-ql$SED8K7Fy^8) z7ahN7W|!By$X!=;J>K=YZo9jUyZD8R`v*M`)UErf?tVQ=dq_QR?CIa@_Fh4~AL<=) z$;+1{T>9mudBKgrYcC7yv#U>D-;VvB>X&x;@yj2&BI(M1uB?#L`}+^rHo!7)*q~nq zZ4A*39z5j8ke5Q24V^r!*YKZ*zZ!N&xF#YlvUk++s4t?o#ymKpbfjU_%+Zs^jED^x z+b6EaRTquxG`{16P7?#;gA#%#4NQzmnwWg;Wc`%oDfgwmboFP~982ptZR~VS`s!=9 z&G+GEI^RA!&&VtU_Gjbl){FysJw?=|2iAx^h+DJGS1Le%If3mz9mWcmLX3 z?~8fhr}A|VrLOmR^oNILb+#K3LV+pao%ktF>vzJBNn zGmpP`==7xGu48Zhh`b&ArE_ZkH?E&_6z*fbp7-HZcP#k*!Fx`0z1=V7Z68GRSW^Ep zFYGf%jr&WIibv!%En4-{n45pMo~imL6rawKq%TITmwOcd{e}$p9m8gg?D$FAgZ*cR z6)sr)bq%@a?#-HGv<(5oKLCeOT0K2lh4XTHgH%q z`-S7E8D^TzGWNoju#W{mvxlh}9Y{cj1Z(ItOXdlG5-ix7jSmsf{502LFhcu51|`t> zAy23gX5eSVE&!UjGAABVUG2-m!J$^1gH4JSZ26*PEGi5sMxkDTn@C6=PP_76-nTS}~k`~tfisvRmv zj~yEu9upN66NWz{>Cb39;>flsm@}K?8L($SY~ktp0;|nzY94rWIARzhCzy0lh}z^N zgE3!kZSHLpc|)X*5_~lRhgD@@$;b$5hRIW`W`{){854%%wzd4j2I`raZI>eu>uvgC zI*%0_PT09QTs=<9%e;!*+;Q{)t&611B}IZ>!{@by1|0Q`s=-@KNvRs~RR&O!MV309 zpW%t;T&x#4Ysp5(l#mbX-_ej2kyi9D-Nxs>vk7im}9|)!^h>^gBtKg2%Q4P>jqg%etDc)Wk%U zD(=CZ`PLWa>UG#vDJSHjRp4Y%*)0TOa;uLdeGyJ_PBWMmx%i2kyT56);U|%u z65$q?)1Onrplcx-_jo8Ukx>s;M#d7XT<$Sao#Lnnt6iKn#CEg5+Jjp8Nah>-!ma$| zUp?neZ7He32u_E_gi2zDaNt3yDM?&knkw3ns-j^2+}+*6>+zW!JuK{@<+Y;!qAq1; zR6LymJ!3j8B;=Wn>d%#sNBA}pQsZ%*0^-a9r=KVfZ+qPG+La3E)yzH86%}-cxvDbK zGg^GtgrEFN{RPMFqD6zB7nqr)j+2&r${YKA*8op(LY`bWLTY|QV zv^8uvqG7^~nYqOeHbBEkv#8bboy(o_%oM?*9VKKWrlz6?(KiIxz#?h90hkv(`p9Vv0uES*z z^+rsd`4S0u#Saaxb`i^umq?(7_ZAiMH8>K3kFV5ssB1ZSAEm2v@bO<22;BX9F=rb% zl+=d17`aK|4Q?(XWP#kuN6%OTZpN%V!RhvtAC;LPpP*5=IWHUy6Q|a=xpCPCJKMbmifL%pDo3Px;qiNf zv=_jjfwrgR>T#yBUWXH%b%Ks|cDP=yo$&B@tz&=?RqQD$8FY5O|lwq8b1nrVWA}(JqYZZ4vAMk zW`&mQE>Ai=kzofNb`3#8jfdnBG7W+sCb4?9^EfQ!(SXS!W`j zN-E-+>z&m^ftUMBA*-cRid~}h0741hv4ebc_8DpN#8DAfr%O`C;K9;7yqhaUL$|pW zzg$=DHB*vUBa`wIrY1;+#Kd&THYqhzqAG_swxph`FEOJZ3xSQ8oU?!P45G3%oD__Y zhZkxrV2V0@akD;3bW#UUInOWRQnHK0nt8DccodZHI3|$g>6ajzSl`l%_Qm`~k3OeI z@;96%*LIZTFZ_J`Y0k$N>M1{WUcTm>+2j6@_PKl_zVj&t#dlsAgugZkVu54{QL{}y zRAK74K$Ab0%)sXZ)G%c;`!HZjO^emHXewx*&TlV-PD)5ZYZV84Fb*=5%`eGT0ED}ZzRkoH1Qb*yI_go-+sgy9Dn&W84skFus}DDNh(f#1+gQJL zj-NbXDnIkeNCo~&96;Sd4Nh$%Mv2Rf-*7LsC1@MXV|3PsNMCJ-o<40jKOSZj)Y!p% zsE_khJklXi6JKlOF62KlUqfH0Q**~Sj$6KB$>zX2OArXymn|3_nS4vKvqw5kIhj(T zdUEk*2nbpnZA-&+3vHVGr#j_kr^x6!9$Q$vK~`}yE%Brf#X;8$`eu>Zxp^<*vjs}q zn*PD>>oa3N(y_zi=G%!BEqv?@UK5b@Nnv}}sL73Q(KwN-X4poRK zHkW;z`Na=aOXZ$^u0G#j;vJ0)?$Ggc26G%31vk!6ZF5A%UHNJ?oGPM$b!D?7-?L{W z&&WufK8>PiJv=E8T21ym!nK)f8{s+0TT8k1{JO%mm9i^5_J0{{1&q-1oGo-g9IDdQ zVBP8C<{z13)$FE1X))dw3ek!`@F-L?4spi^dOY+pt@u~fO)b_b)hlcNZgv8RVA0eR zsgW|NNzTI&(j@njZ+tILV;TY}9_A#uuq$Rhgz^z{Oo%${11iopn0V5Mt!&V6kP?l5 z*hjiJu;V{{|LWC^z6ab{`ufsueSY@2qvOv$MYv~lgdW*H?d%VJX=mx*pZM+m?;}4+ zTHsTC<_g@uN!4erM;vSY7W>UW;iS$8k*_jTl5-?}3f1Z|J@EfW?A)$mLA%V^SX^Y%ybT8@t&QkB8#z2-MAX zxpNor85kbRd^#wYj#HEAHp6swe>R@b2kv-y%*6AccxGQ=rD2$5O zuG{>RMWn`0y*`m<@Fl!kVWO^aLbsrnP52elRwo%pUE*DQ{R?2$ej zc4g?_XhuqcbbfBO-JIXYMonXp)0L&`?w-$z+aIcIy%kP>ka9Z)yZa?~>HU7{=D6^p(UzXb^T-NQsXP3f@{n@NF-~ ztjd)v&p7swYmmh(pXreKgjjTYeE(R>`iSUflDdAQ>R5#`3t9JGLEsrLNX^J}+8(pP zfWIfbL`(1OGp1#<^j_`fZ0><9(&)|B3xzI>=bFpGi}WQhY~_^aaxwpcgG={|{tnF; zTL?kMIW4%kDW(~+4U;)Rb_uFhnt6b{^ww3?t|!i9(G1k39~RWjoj!erW0bk5T8NEX zF0GGioMcOt9+McJ1GU_YY2lUn>|7f|1HdX1<~gcX4_QTmT-7L;pj$X~Zl}iM0GFeb zh)zokwA#QYTJ(-C`hGnBN-E}m%maT<{>7?+M*t81+=GaxMS5c%(rn$1`jsll+tF`a zL`O`{_A7c48@|}g$dxa(mysJOUde@zcc^T(SmNrj8T?xx&SZjD^J$yrMMsY~T?Ci&Eu%oI6HO*My#|b^OQd5Yt$9c;9ua`cP*b7%` z8Zxu&l6mrEn`FVkP?9BOj!h^!>5+A5TW0bMuJ^Pj!?QhgYO=JHg1KExA3wVQI&V3P zUb{WdaX!yL8(^-&ot~Mic%H%p99MY$>d}1MnJ@fGFjmtmx zptd`v-9-jd#q?F3l2elilNlElgr#0#TV$|U*x1xk|3lm7A$VhCHSCNR)NF~rsP3E@ z5EGH99$ahqL_A!oKTk)&#F%??e&CACfe(6d%}Q(eX7LhJ69Yn<{umSG9?$KM%tc)J zw^89#=IK^0_TG(8s^w+x-T2{D*0R;cPIZ#UKefH2fJSvU1rCMuN|*Ovk9DLL((zbm zQr*>dp$=AF@16BS3k6Pbg|f$2HqXVcc`!G5s3p3(@ptB54B^x~T;sZn^Ro)#k8Vs~ z{dl?i{Di_g2aGsXjPhV2t>njsZyZ1+fWRylh==45-u{VAAr)F?3F7m4BB2!1Ay5Q+n-ES1`83Qe@smS==-1%s4-*o9BMEN!MX-B(4nt4 za7`9tr>H`fjzRDT^(ArHmgC}t@JO}=lB)4}_`^WOO|)F|@fOBK7a7P=&|S?-i~dPv z#Fk2pFf`)<+Vt-v9t*|lLCvqbj3AcQiB(F8ZcWVXJqCI8+aO|L5Xye72BW1+Ru}9W z(0G8nea`Th9XqsO@VR>WF!X3qZXvTm4UCxsMn84lpuQgvA3Y!r+659mV7vwcEbLv1 zDvpmCA|eowC^uZx(9qy|SsGt0SL7TBDod}4+H5%%uqtI&Xl23S;PR|}u~o(O{wtGq z4%(zU)N5Vlo{?MaC%ddp+a30t@o2yG^Y@S2zP!ozrb+J%cs%!~9{0}pBIaf5@xVK- z{%F`U3;*cz(A;n1USE2;+8|47M<#L_q0zVUNHT2#iQ5#FyXE1 z&-&b$@OJ;lGzYt{x%Ttumlpri`S#Qghdyoi{jvw=d^2`iNkfNQlHUt?LjOze`(}PM z>J>+A*Sn^E68?PQpO-(f;QR4!t~ld&bK<)LH)?DZaEiD7~yk z&Z#&MW!ZepKWXL8UYYCm4AN~nG}6BHWI)QQUBOx9`$7vU562c))pto-y}RH1_4~t& z&mA4Ne0!5m!i{ftzjn>%{WXsrj9&cGKOK^9d9U}(`@Ra%Kk>^b$1Am+Q*Zz9vN;ca zGt}_(?_*20HFTYN*C&@Rc;x%=!sq`Szv9g^zLRcxr^k$YzZj7F_)js`mydUxeCzv{ z%)0;U!Ff;qI=bl9Q-N3C@ll_-4}Cjq;WK~4Eq(oTw`q5Odc}2*{t#h$;janTzjf9x z@#c4XW~}{kpmyWWBWznvbeeM82ba!%VDFIpr+yo=g+4mZQ8nX z&!LkYdWVcko~d`-a^DlLy!X{FwH+@RJbLo1yrNt0fAZD$zy9@9r%Q*7nKC%6Q`-4w!`|z9J8v^?bi@SR6!lidS z^vvrYef!7hi~0_~>Y90rmfiXAvu}L--I2yF{ldnj%{MMzz5coFyZ0Y$>Uw$j_^AsD zSKRf;^KX9g{hw#LT@f*1+I6Pu?|$@!w?6&hud^3l898x!w)uv8HoUmwvmgIfRQ`au zvJqW|^;FIlNvfb8=v+o&6IkN(P!u}(D-TDk3ZM4IRx+!BL{q-X=$c)ToSsL_iG9tq zJvz??v!v-)CuyV)dj@q4eD~dVDc?+F&gL3n=;rva5Xba;#M#(UFSBVjK?(~x(M=dB zdhlYw3B(wSIWz1QF?KPF4>}B!qNfPhNNm8T`?#iX7CsT;TG$a&SRCO&avfKx2?ZJ# zpJ+bKS2!s?{w;0{^ITU!S(t5wA+Z`1)Fj)1wib%0{Ji9hvP&=qoAj`SVzVvAJ&_ee z==6s95vAjtFR{R`RW`^68kDqsvSc^M2mQnW+br$hmZbc>_<5vd79N>WS2;q~jE-TX=KM zBbhdW`JZmW_cz=J-z8`Z<|Q_H+<19J%p%vPIY^xaB^L{XKdpwFFkX%sHTwL+iJFNe zR^ZC1GclAKMB%cXR~w3BTmwiXT-^qlJPQm zc*11NHzAQ>xkoiY=JaZ%U#p^t$I7F3`*Y(1WP-+x!YDIdD~>IMYjc6KE&pvK!|LCw zJk;r3vYkh1S}ecnzpZ4{W*{wq4U`T$Z>|UOmpi4MJJ}7`Db>||Rrl3bU)4#|@DJCy z^u6cO7dO{0d+MLLMu{3Lw+ey5HVhl|$yf>!6S`0_Pvcvc)uxZ$LGx;~DOiVN=|C+Y z*Hck5Bc_Z`OJm%Eg*=yg5y&gNBv`(x$R)LKAwE}E=-}q|&iR~cl%vi}D;3<>o7Ei> zPZje5Pk&@k$bGcz?l}}t8Cv$|Oi$Pxsj0T+AL%BzF&VdbSxC-|_&Ltb1Is9NS9~g+ zXzEJiG_;T6!{DC&1Ve4+MJ)##7M%7M-m$pSgQ-I- zxXMgOt53E=d}4#<GdDyt_ZI9}fZH&{Z zC_oLN`F!2oejRO7-2;*8_Fl9(gJ?3L#Nss2@)_+0)f%2Rc4Unba4`dyX5Moi?0T=# zoop1vXC*yqPIY(Dn+-C|>JOS?+64fD_e2)gtiJW|(WAND zjw>2wEaMDH15B(~?{`a)EBdk6?{2WPETQp*E0OIS{{)fwy?q*Q%m`2J-l1M zw&)=$-9`Tb%*D-Uw1uicr91g0(zLcz|NK4E)_AZ$(#N{|d-r1O6Y_9(wHs3Zy27>g z-Yq^sP#(_xE8k2D)Qm6BbYEW~y&vZ))L2OA%)2k*^zi4g4&%~^vf2^plfJU0d?zwH z5#DZ??l4qnBZyi|h~r(%BR$W2kqX?IFSXyW?Q72d!SY0UaHqf77|%U1Emv*o8GzK|piv@$>Wb_v_HX-yebb>AfD_CI9XndO(ZOvFEj#P6HPA zih(X8VBn;e-tIl)^J9ZXK$p=u@RCb|FY6OBcu45bVSM*N9Bn?Bl9GCLM&`^}v$4-b z3rQ`%z_8F}cN8rtE?Ii>%3E%|ZPo2}+`D$&efK|5{@_E8Z`}06lTTGX{mjc-w!ZS} zYgOA`f9Ktu@4f%Qt`9%@;>$f>eZ6E}bg{QBGB-~agM_zCP1uB&f2?aC|v z<;RQP8Uz!-;Jn)i1BM*lBLnL4nVB0UwNr0Q$MFA%PS}T8G^cZ#>r5=X<(6QqcR{%qD4qbgH zbDXrOV7GpEZYB;U+7YrN@=__#H$dvx&A&@$pNsrDbnSGppConc2%9$b@^;6^xtP-= zbH2ldX}8Trhs@J3`~(q!pMpa+5&71z$$${?AEXx4=X@s{RHJcMq{wO0!$6D zyy~|34>2dCrB0caO@~&~c)r96Q9Vpu_{AP62_H7ePDo3eo|v7OGIQEBEqg*dJb!|R z!U(xZB^R>zYf-L3jdc;E zUv*Il<%PZn^B3kHrd?K=!ib%0akSN#y#PUR(l*!VSe?rjAx!n1L2E2D1IFjLAtqp%1Z`L0(rYpmEtb3Fo*?P==?zQ2obygt~9l3QtA z@wtaWB+h`TFloVATwEVU1GERJ(Q2SY!a%MTOJdH>f(T<$8P+^ZA?T_Tdja;_LV~8o zL-pfmUO(&%VxI3JgVm0Y39GxG@WoVY&1Kglv@qn(g1x$^qGtKy^{FbO5F*0?p#%pA zpf9rN1dIS^=OuMp7GGjw>WtA*(PRqC+XeYD&^jDGB0<(qC3K3MBxOQ8Vs=5Sm7)oV z?qtKJF|;+zjX6!++k@)Wxz|$6(I`G%&ZLAyG&#~)sswetXBJ6vRB;H21gig(%$EIM z>-|0%n=eXu?C8mf6>L*u?1dU68ZWcn{2YY>tMnGvO8zJv=oThzcAz!=2vXHBx^Dm zZoS`WIHNNv71>Y)wx;h^9Ki^ z9zm5#69E){66|J)@$dz3&#{7Nqtc&~9<4korkF)ZUd8DIQN9D zyT+Y8BZQP|P#?JQj28T(%CUD4oQ?G+HH@uUywo(BMUD2S)-R(M>s=rix9i8w#3g2o z_LIS-XgFn@v`zo2F(xDg5vKV#hhc)aavY18skz+Abw2q?J@5QbK7=7Uib2&Oxy~Gf zAv=vowB5|ihJRN25HVhG{>DvUNM+~Y3-M&=^@~)EYD?~%4+@oQGY_q@EsuQ$e7OP| zSo9teH)xY6@k+=cdXY^J`UHPmdlcnoFE2@UejxTJd}v z>#!q>1wbl?_-aFJh0b9W^yT~B5L1+hNM;Jgt0$*U9vUV@Oz}}Zr0eG2nKBQ+a?~tD z68|W0f=k}dWK5cOia5biAa8SjD*JQyMAfIdd$2R^2|NKRR&Tjd#5V);4mv<2l~x-e zIXDY4876}|Hi&a0Q`KR5F0s+(PPD)6SR6Pk(l&mn*Z^6NDC3pbe&t|;fcrMM!%8x zaCqytBr2SSOt5(5Ydro8WB>W^As9t7e{5{AfsYya=}8F6U$O2a#v|7{X#!3$@+=J5 zcVh~LEl_;Z4zHIP8JS$ZC^Ta~8D-034Cge2Q8t>LNk?0^oYI+<5>wfll);)MS`-91 zY&KK71G6+7PqP4SyU2dJglabRVI-vbN>qhykc7nT0<@nZc`@_E6V86B1M@`#lmDZ7YpN)F#w;9VVuXtTRmEh$2j6VO#&iYC7 z_DaLH`;pdOy}`2$6Jc73M>WH2Z+KdmYcs5eUf5Y-P%6-zoBzl8^EMkD7?m&2Nz6^; z`d-e5Xi#Z|4|zE``7h)DT!3=1b=M3W2aUPThjUJ@F;MgU;aWbbxH5`Nx~n^QV+TDG z`F0mB5t6)#w&TDwhtgy$fZLG^Da4}-QFXKw9?aTP^k=F8n?9drmMl=q;m8bQ68h{l?S@F3oUnUIEFV(qjHz3dQdPV%X{-ItYu;FP+mzRqgdPswH|S8WJ;RUm{cgx_m+q}BD9_rY zTbKFlqKD@_mH)tO-*;k;cl&h0S)aF~|LOY4_%k<6df9sSw70ImG2x}fcTIhB#ekoB zd=c^26+iSp*!}bHKQI6O@!Wf7ykL6ty2mtYu6@4nkp+Pt#hvc>{^(PkKOEc8;k{9{ zcU=AY(px9LT6BBrwvt{>-cz!fiay`_`@E^47ulc(Z)-MSDho`X7e>_fE5oKkQFI} zD!adz)qh!&Ew|vktR2Z0MO`1fA*0EbP?(v!A!AEILU@l$CtZ_UkePdZ)}(9tU-f$E zyS==_r^f6^-jdM&s??a9hK%WZpzFo~7eyt6zaF}Az?i-}dfeT)vitSHJuZE>S7rAD zUGMJvBUK}=V-x?@q`Z_KeQM%I=BaC5`0eDxXSy5z&aF94Bx3GY?Rcu%FzEM*Z^$1k zTYBxmi3^^!{xGxh^NCWgue!lrMZ^QI|DTs<+Su&ue3K(vTU;D7GCLbgA1C$Gi9Gsj z(n@DD`<7%z6!yp6|7cJD_()ju5YE`+;yiu3{IYmk_2(Wdd}i3klI;vTRE7d(*rP{) zvEv&%ZZBDk{Li%LD5P6ltjRSLMMsay&c^fw*4(heCquSm$TVm|lI|Bo*>?gsfyCJA6u@GrMqs(|i=;^_XU+|pqzwY#mg2?E=>2XDLybSg! zc+;LjSa7zgtg3N^Z5&xZ>UDfwMF)A6)T^y|@hEQ$Xk#;EvlANGcS_l@HXa2Ay-DV0 z4wOEgskBIu9gD^3;r7NO{^&8FJstnpO5}(S(<(Xw6N(023as=98W>BcM9ti}&7~d3 z)1d;%;);!>FuY>wJYs|F8p6rZJUmTOu-sF=7>V>}&f|9FviS63K_4Qa!^StU^kDWVr@9ui6 z_w-p`kA=979l3kmKe(j2+FC-li@kR}yB_O2eKLo-e*krLBS-!e^^@zuICPujBR!)W zR4$WVRkD>ke0&uL&dk2nr@QiwbX*$a)79r`=^H81Cjn#K9E_QIC5f$?uf(_%fc5p} z|4u0G|E5YOltt1i{9Pg~$6xhdIYK@yy@EGyOP@%W_#Ab*r8s|)-$tqVKi69x8~F%# z^CwBS`OH&V`a$mRTbidlE8T~`{9g~a1Tfd*5{3Vhnt~Ln#9_pDC@Ior zWrcE9dR1C2?S}2a;xip!0Wlh*c8qkr5=GKOzdpe<&PV-;tIwa^2`C2r`(D>I?-#zG zq4W{zbtF8 zlo#7E@Q&o}8A>PV9?+tnw6JNQGz+rMdysPyaF$7_|F3?L{wt(eXD*jkN_p3GGR>5V z8hfIxESA1KJzR1~cBN7pp_C|(Nzd1JgO!H;te+Hlw&ZLeq?jR6qBO}@<8Si4+c(xf z+5aB@XZ;naug_?o89s$Rt9&;4?C{y;^SRHrIIsPrPmnMDlRf^8^c~5bOQZ*+_0m() zAClbR59wF`U;R6DSnOZj;kZ)K;f)TO4&QZH+`-;qt?ydj`|)qM-xR-HzMuP^^bPV$ z_gmz5T)E9}qu*P8-}w3YzmL(rTVMw!3-*(aE9d)9Mvs^UNs|8FEZr*%R^> z^P(kVaa=kv6S4?g%+)#))P`nqHyY?Kjc4q%Y$L3QtQv&Hc*fo@!&-p`7J^tR%qN+3 zMI1?;< zu%m#i3=PoyI#U6CNaZRtNLm;kt689Ox8_Fa&|0$w3-R=uF7;VvPm-kJ)hy-P_zUql zDoJLiWrD8IrIQOj%fl{*ILR(gV`Lk8QKx!xVD!SmyV(J;N=CcWYmHP_GEIzdMMTi7 z1ol-%b1u}U&gMWS0mD5l>SL6AL`Qx3h<#6L@-(Z-`{L#;B)HZfWv)2>EduQVOt&BMs z^A8J8EZ!7S#BTW!Z`+KRqF@)#uPY)J56;GGAztzJLgIBr#PjR*EE2zvh&_X#eNW@% z7)%pkT$&7y;!)CQ_%n`TWtRX+U1`?{zKISA3*J1L`$9ZR`7bR^ZgH^&ovqcV`&+e) z>hzwwU(Z3(&Eh{IMFw;teJ-!hTOh0cM7+8ZG;cH_HRfTeyUO30oG(i{(SIX^2VId! zNBGxI=Rd9!>0YP4U(A1u7w;zs6l`9~7v(n!x{VvhJqU2}lK}%{7LifpV;m_Ta2Z8- z@hBCh^7r8)ox(K!JWPES<54(&EftD#77BWX3kQ>+8~76&g8GZ_{Y5y_68;13?1y;< ze_k&9nMJ)UUoPHrkSroU!-YSKNS}|w(6vn91h$CvKrviP7E$hS(Q$+VL9$Ne5r>HQ ztb&dc1fi{>o-x^^r6uSz?asQUkRc*p6GXf=;UDV)FnV)&rR5xIo;J}g?SkX82@>M4 z3wp3;8TkF;l zFv#)4ph=sC6P$ID8M~WSNH;;!UoSl)y$C7qL+Q)~{QE{aBps1D;n=HAlmXw_{efs)cbrhXpoWCUHoH(f* z9wiVc$FEjN2bOiJRmy?K zf@+n+z^tCNN)*QP>fW_VF)%8)R;dBT_Ni55G=!9XwTcCpb49IE4csLoK6L+H0}vlD zb0Fda9s=$IZVEwsU=-U!Yn4^N^x?G%DQx9%#1E{AK>Wbcs9Gfye9SnqRw)IRkE&I6 z0SApix)771#?~s8zylKzJ`!uz@$e5!Pe#1J91ZdhT&Js5;-Pe|2NnPe@{vzq>B3qi z7>eZrW35sK>{p2RfyHL{M+e$uMSg&pcH{?mxTsc183B6VSgTY3b!A8w9V=%Y$_cD| z0PzAt%kdtH<;(}+pKv4m15=*C`_YoL|G8Qv3ut+J-who4enp#aYbKc$obYf|tYxP00vB^}d^LFuOy`hMj64D20*(tB0r zDJ2DXbm1wb0=V6HN(sgQbIgWkpv8VlIRIQ$d`gK2-_0pSc;M!{Pbr6iN7vy!_-Rr( z-UBP&MS8&J-aDm~f^l#D2>yUUpCFxh)YE5(7r6a%_ycy?gM1~R-cFoSHUq00PALJC zBx$v8ol*c?*R4+31uW}Xr^tzTzPwH;1|A(yr_=xghSVvcNhsIwI;9jiKN9bO6(j1D z;AFI?F?C7~aP_!4r4ndQtW%`P=s&r2N)|9lTc=b2ExJ18Fwj50PKlp_c2iKNtOD*_ zSf?BS9$SQTQV_2L?}6#}!auO;QTR{A^HX(7IdId9bxJ+3W;5Pj4SWmn0}s55_k@G$ zm8@$}?!om+1#mU+05CneUWrPBdt$w^3YhaS-UEX+)GP5A_clL{_rSEx^~zyjmsjhR zbjV}I*YF+~^;W&I4_N;`;+c;2`boV~3M~7qUfBhV+Jkt~k)M6_N)@o;2>e}(^8a10 zlmRUr8Dkq!ZqtqyU2=5Fc*5=fa$vuN24yoa2e=DZ3OoSZoZO(q&OyJP0)N1& zRJ;dvxd!C`mI0&Yf`4Vg9ax!#ass6}$TzSQNFOlpp9i`Dx6Vhpz^DbtCvX+8258ry z9_FLobSNJ%Q;+fhjle2kMIP!AC>Owe0oJ${B0buIYV89aO6KDZSIVc}63MdsL9$-@m=nPC>3OWNTmmyz3X+?vw z4;TeJ1}q1LYLL$LXg|PpHz0kWe<|n*tO6bb?gs|tf*;<9bbvZw7O)gp3fu{-1Rem^ z00VA9{(uF*V67zW0mcH$Z$|qBJ_jrVRs$=6DJu~UxEfdw%((^m(xDvzY76Uf{%YaqDO5ko_HShp%AMhmb7_bQ#un2U!AN?KJ4;Txa53~R` z11kt003CqGfI&tYuTgHG5lHixDqsb0H?Rs=4?GN9^&sd}2ssNF58MpQ0>(dt`~h<+ zkiH4!cnt2qN}vUp_BiT+o;RZ0X5?oR+zDSnIN)=@D&Tft4LxrIT`XwNuOoh7@EfR4 zU@UMmaQP182PnUX@-0TWKL*`_>plZLfQLQ@J*{XDd(j?%S^H4`z<%E#e>Rlsd!!Ex z`VsEHaR-n-Fa=lxto#Y-+mYVSXjj1CLkJH%0F)izFTWyRz(KzuU0@n;GjJ2|0I=$J zv@hVvzd+w2@QGunPvCRFV&F+&B{1|K)FW^{a38P?cnny10(4#i_mgPfz|1D(3urk5 z`W1uUoP|5k2#g0-0<(boffit}f_wopffeKq+y^`YPS_c0a@CRI- ziTHr~XPs8CWFz^{Mtr~&U_8(W%mQu#T7X-DtAOQMh!1!a7`y`fXD-qM?g5qotLMQT zxOx6*CE$AWx9gA|usY|oQUQ$8z#SNzi~QVx{OC?A>A?6rxC7Jk;SOAGL_SK5A5ZD`;DMe5y}ZXx&-9`rZ2<$o6t^IAiuz)*CQNoPbtcIGsdl( z;SPN6R^%7BY8Bjpy>36P90u+K$}2(7dr*Gh{@8?l4}dPf zv~suuw?2$~1CIfZ0V~&^Rzhz@{XGKu0CzunS}6ynRlpyx3>b79_!2N4XnY*`0nXou z`~XXV`+!YPzw3@SsufT2JmFdkS6%mS_iT7ZXu6~LU`rH`=GJO<1GZaReYfUAB%xqt<~fiCwU|G;3N1sDZf4om@VK7w?CCxN?w(oxU>7!0fj z+JT|>!yhmUxE*K#?gFj??g3T+qy9vD02+Z`-3NWiH`~kzdH7c8dD}l#=(#4HR=)=e_Fb8N1YE&wK z%ex~!;C5ipdXS|T+=20zG%Dr5<-u?VW?j~(1V4gu^=VYHfT4XGl``PgevQgu;PNXP zmDoptS2ijIz$6**1NZbtxqw*%5&s5+8`P+z1Jgnf9#}CL@dImyBL5W_FNZZMtAJ(U z2nQ^UXjJ6K5MLzn1soTJaKK&BjY!%x9*6(22oKy1tOD)_)&TwEKrf&S zlsCdZFcw&F74l2Zz*3+ESPt|b2l~lSqkU@Ts{@_ehU8PAU)u! z1)wKzc>(eR+`kC%SE8N@@g5j!MmXS3V8GMpuO%oKaL`iF3s?i(1+=U{{+>ZQy&mZU z18#&r;MSFBhrlkkAb-Gkp!_W8155#y0WH9w+mIg62;2wU3Ooip3=Dn_^SwJke_#!8 zA5gj*`FS4oaxd}=v^)Sh1MTaPAE5sRr1JvW@kYcC-2DXd0o?Z-+RKZe_X}umK;27t z4>WGa`^_jPFcuj6Ch`y5v;*}F40{Lhy@YysANc^L04sopfd_!keT;VUGWz3g^b_E& zPY^F~<=1$>1>@;Hya(0;HxqsfdH@6VqyKCLeSq=6AYc}7-w&uaVCj#j7hnxA=oQrK z0pt%@11ta@K8SV#^#2)j0agO#SJCedp+5lazapQ&RllJez^#Wtx7R?=-w_Y+B(Q*< z|3G~L*8{78u}6?U;3i;b6~Y1IfmOgPpzbK>04(?u^ajd*q5ij_-yMfP;QSNlS3vnB z>IIl~3h@F@*2CZHcz*`|fbqUfN;xphze%Y9Rss(Lmv?GXg5E$o4Qx_kf$`m%lmejf z;wGgOm=e^aQ~=X}RX`oE2AJNfNs+drJphA&`+)Jl!%%4D=60Jiuea;QkisC#*@y0+xj%pTMoa&A`f0aNmJ=Cm~+o%ETt+0C4l>Tmqz4>#Ta&U6*y|3&|32ysm;=nY6Yjuuz)E1R)$j-02MqcEbh!)h z0C(OEx&W8ogYp4O%RsMPXpd{)4$Qn4?m#239JqTe>K(WTh(!X(i=YG0ct89DbJio@ z!1a$H9I*0H)Dv*|V@UTS&|@R=2efZOy8x~PRsoBjK>2`&f%3=5?~{lRSPIMnZha2^ zf$`77Kd>5D1FU=j`Phwq4Gad>0ONuGkGJ=MtMe=m{_{MXlcs^Dw54s@k~X#2(w4Sq ziyA9x&L0R#NP{ifsHkYEjfxr-HCCK#qRkYQ$pH>*~YfKii(!D zX^V=AF58%kO4~Gnp5OPn|2%&VhqU$e_dcJ`;W}LBx~}`*bzk>&U-zG<>aQs`u=Hz7 z*;BwRe*^wIFyNkrR|3xcHys_aWc{^(yd}lnZ$DBz(Y0;F=lA z4crKv0d4`#0&9Wuz$W0jUy(mxF>nM}0z3jN1x^FYfG2uk4#17T2H+OpK42}d z4cG+i03HB#0=s}I;2~fJH~}059tW=bHSG?pf13Jw1pR!5d;r&F!2^?Pmb2R)rC&MA z*)HIkwaeKF;0*9Mu`7q#y5(%~apVKbfmIhQXPbcAfCmJYEN71ZH-wY zxqLa>4%~k$a)2YZ!S^`zUkN?1^`*<%W5C5%FK0JCLB8V911FN;fwQ&H&och(gdTVh zI09S%9sw=_r-7q&7%3v-2C8jcHK|N|7Vu7wZM|kA`jU1x#jE$V9^(rvs-^k zzIvCl2Z0Crma~U}bHF)ZN1F70hJFBRfwRCSU~xbB1*U?z<9uzVi< z9$e1u1-5(ysY{+N1~{|lbYrh%=%!@vW;W55pJ9Iy*`3OE8RK7oA)mILR2^}yoDv)Kc{ zZNM~eKkx`}U-a4RNnqvLXR}-X1ARZ|+3Y^x67U!>vJUycQQ*e^g#TRR0h@pafX%=z z-~r$vUpP65~aiuM6+1V*kPpTNda@&!BzoB-BuqJ4l3z;(aoci?7VGjJ=g1y~Q< z58MZw0FD5sfJcCHz-eIQO6n8154ia^v=?wIuoGAhJPbSl+;|oB2b>0;0v_88-6H+y zYRUy{10IzBuc6+6W58qbe;MTh9)0n%+1e+eE2qAJwbvsLI02lK|8GDpa1vPj6y>>* z^nrC1)Dv*cP2?BYa5MRQn*MzY=>m@d4+0yuP(I)(;1S^btk^Xk{30S;` z|DR!;xQ~1QkG-9G1s>f;djYq-2l=P53m*UvJp2Il3@mR$-_D?ae?a+w6Q4jIfQw(I zTubEd=cEs8|8Lr38M}Uxd;uGOLq32-Pl11ydI#16Pdtr$V8s&hfd`ktXUP}v0B{jF z0xW)(@&Gpjr-8M=6TpK&c@x)xEbRd7@3P=CxF{7 zM1Fy@rmh4$u>4~1z*gW%VC5y*?6!!rX7i=lY#Layk^BKS0#5?FE(0HR)^uDB-CAc& z`xT@IJWxu0&T-acfJcBWn~l<@bHz$1vXzre$I8)Oaiw7%Qiy~ z>;QHFTd&S$j{zeu;+<{l9S(6(FTkcU@(;iU`Q+{B@_1WyYLdtss=>sQjBt77k zTgfMI3Rqs`tT_(c2HbcXa)4vN!@wiJW59XfNrAU#vzyPOo`G9|MU~_aC{Ky{ODjKc z*P6(IE1fHfuJZ7ud>i=zdn62vfHFRne7e9_2_m*(^H5~CAb#GaShu_5q1caps3Zto zBcD;|k}K$Th3Go?ed2k@8-s4%QvBY`@5S=Fc?3TOz7c$}!7<~2cZ4^H9NSjO=d`fVE z@=M|4J?@>M^4C2aduS%+iI(<|$9bT>PW6Cu27mYpcg+>HA4qT(LcV)z*1gLza+(Btk7W$~W%em%qr6QTcvUF4NU7@Lr{h598Q z?aol~@cEMezV+#IqqXazJI;ys6!jJ+yEmlHi|$HY;qzieoA$?g*7u&9>@G?bCi~W<&*@(~5FLzU3U;h7x(X`Y0Oa-Y$6~Wjklh%k zQ0OE{XGtCKCR>& zc7`q)zF2kA3wtj}beE(qO7<0}&+p$ba9*pa2AwHoPh*AB9%E7L+K<`mb}s+KHZ;YC z)(@Xc5$j{!>r&?=d)D?w`y%Ores{nrc$-7{n&QFYd1ZrJm2XMf^**B#EElw??D5PQ=ad2NarY`N+kVoqO~&BFcjvP}J|gT1osfL|*UzM0 zy08;P!-aIBrt0qXsdIbQ^`6tWHXZGc3=|ByuSd7KoRDq>OoS&liGH=P$8;n58~M7D z8?-;7sWqa!qPAj!#QZcVwXF^I&!F0_X+?SlxfRQc*_8B$2aVjE{va~92klzx+4{rX z>c{F;Q)O%>-4gV%mc6jkQco{ik*;S?tApvjMu}dqvGN@Gi!s{fPj$Qt&5 zO1eM$skF~w+D8VRqD}2itbg4A{gtt&Mg|-ic?Q>JqEaTvRw!iYksBQrI0(n z_Q`C8)WfTlTx+uuzLAfM`Ff*2Ry<}^9|^iXXOVM^y~}D`EL3uWdg-;}3mF$XkX_06 zv$XEXY{VshkN!m3iPo`$QBs*z$!ConjdoGwc1Mh<7}evk#~x=;R|9QK{gMne-j9A5 z->@c8Sgn5vs!VVIlXg8yKHAr_mzaJM`GCrY^k=Ito|cHpi0+8CxNk#lPM;2TyfWmL zoJZ`3z241yr)+9|qlQGc!JD@<@>A7%XfI%Ix7d{emHx?Se8cqGp?40yLm2v+)AuI2 z_oeQy?tk0BTYKK#dtY*}Df5=Q)}MFPN`2jMGKxKl8qY@LXOJ&@%onae{&(k8JxAgj z<|0FN!@H2bt9xf^M^A0<_P&~Qb$@aokr<3;Vh#DFw@2`b3XF8EA4|yJf9sRkO3~l1 zEBPZ%B0S!%urDptAER=x#d7PfVFUgnd(_WXf6{(!*!F|asqy}PlYWkU6BF}7#(R-F zgWQpqvX@=Tl`wLBfAF`q#}!4y9@%4mLi*)JE96R5#U;PW*jxYPtbXQRRhc<}#&nEfCMuv`E4j>-uFuE7xhn?A)leP$9;2XpoT=A20oqiyE4u!C_8=TN6$0AXSe@S z5A(=vi#@5w(SK8NSNN4H^!)#F9F_7k(ht%J&SuchA|F=iwim>QZXUjg@!_N!>%Vs3 z#fhF9datkUu1MY3SDwBuK3JBy<}T{Ey8zpW*Uh{>_f0{V^ zq4l9jd0Y~oz}Zq>m-V>Z2la4P9h&+yiEmDpL&!Qa8?>VdkpgAgsz`5!^yc2i85zC@ z^w0Q-4I%Xt!9hPU2}=q3yZK%-{(hbXBY)2Dg2vxQW!<~50k-E z1p?IUUz{=B5my-*q(8JEw~aGH%AdXccO$n0xs%8}a=`c_=PS8*8)xxHc1jQX-TNbT z=w0OlW<34Mtmw`E^>`{{=qPfFKlWs{Sp1AnD7kaaQ0ed$^!0du$-qU?og1RN&PzsX zi=#Wv@4F;@arNMZ8KMf8r!MQ+*n8<~q*vLn%-UTE{UZ5w{I=s-_Z?<=CUmC+ry@94WD{qp{; z123!YepTv~gD=gzM7e{uMyQNH({Jd)>SJkSiY@ssx)?uW!&6y_Gexde=}ZLdii%Qg z7n>p`B_UQu>`DWzKe1}-Cz0Dsz_zU9sq7*Arhq*rG~4UQ?7uBsp*S_cxF0+`QV(O~ zuj69Q{7L&9G5uW3Qi4E&ObNxwR{=?>WaaG#=0`TbrxgA^lZGBXo|v^kMimz;xrF-I z_>`W1y;r64Tg|^3k+bzO-d_le+@a*0q&}tN7HyhyV$wGggBMCSO-iqot}I6MWVkj% zA%|A#VH|nMTb|PM^RFwpL4U@Z*ZnT%=N+V5aVhYgimH}zjWaGASE zSy4Z$oM-K>)+3gFI(|9viI;KalJ5b1dS2suBP@%Q;~@MSBa|bm>TfQf=P^<5xQw6G zqU@sQaZz;lPsuOMc2+wl{ip$b-}Z{9vNKXoPs}Pi7pSM0)Wt5T1*s*e38}b(Kak;h z)p1|^vN7aN#GlGm;{QeFl-%IBztax+HjrHKA9+T6}s8jpd^%0t);af47OHYKeCbWYNGQg zJ^$RjLVj3u(#G@L?Pn7&`ZbQ+NzP=~OTM+gM?}Z-Pm_{c&nuNNp;~2IX5y=6TjUm% z5>x6ja=)+jD^MTXMXrrJ)~H~ll6%(tirzLOcl--aWjk3~k8~@!?dLGxI%lYD_@m0V zcp&wWM1SkR2aA09sd}Ek z-dsEUVwn%Pv7Q(8ULEf)OI?%f+nm0t|H^?)?)#~WiSQgF7_0op?(h1JM6Q_uLM#(>nU(*#CiVw$>U-PQo zSN2t zTB=0qCL#4QSem&)0@*oL6VPs{@5(Fj^QP&aGOsJpeo{ef=(gcoWgIMs_1`dX{r2uH zsatw(?!Bq6B7NgvdFHx;4~a3JSZ!V}ayyY*@;G)=`n~oa8E(9JT~Y?2+FTJy$gM)3kN?!jz5I70SH`&%ax=fc&y)O~qvY=QhL2=$=#ZgAVrctALq|}PlTv=< zb^Pa3*`&z3;_UJy`W28SL3O3EBBx?A@#bF|IWPO|<+LKF?brCjlApVOJ2_JSN0Br8 z6n9Rj__vZ1(r?k`?V{Ua0BqM62Xl4x*7N_XnX4^7l^wm3{N1PI5+53R>F`U$o)pBo zD^s^u_iahv(sNtyt%?4d2W}dy$lU1u3zDJBuP{=7Eyz7`#nalaKCa}xV82@Aj3Xyg zYUKRMZzo6UZwWc=+_9nJ5${!U4hQW0Zx!c|{58CYwz*`SR-l7-dU*Bi+;+!ALyydsjiw5Kzd^>l^5O*myRp_wOYayu8q-nlRh)!llDkn$G*UyKb>tQ zP8q3J<(i=0ym+*@EVVKzRq<#WH*y!KHnbssM^g3=x%E#!t?smmT%+W&&M{Oue7p3c zg1YY8Qn&VO>Aj`z=JZYd6$3X8-jKPz;N@!0yV@|M+IbPV2ktzTJuLInr+*@Kc#?Hb zPd}AfqK!$Vs7g0QTSZabH|e%s)w5Ohznpm8Yq;}B z_gs|yL(b;cp2}vVAN`k-6O6aSBy^CFU_!M75+sNam#Bifgiy>({)!cy;c4cxg#5<0 zoytx~Kl+W5Ke0YeV038Q8UAaD$Huz<%1QmDbLZffou0!^?^m6^zi`rj?(~m217C3% z@N?F&WzN1~Vm(uvbItTbtzXKE_{Q%)#olky`GHDjTTE?IE!wmtCJ{jKHMfZm=T(5{ zX$HR8eW$XM;@^A&K4P8)LtBP#k-0(N4e9H54qlsiaZUHlshfH#dT;D6AGppvmK&T1 z=;;FT>fg&9QDQ%Ttn%T>+aaQ?B#~V!;-a->1#6vLk->O}>>R^=JXfX-pospYkT>_SQ|x_5-c?Fomovn+64_+kSCo`3CAG4- zq$XNhxZg-(w`CvP+t*^FITUpFI91h0#6CgD^U$Fx0 zw_7XuZy-K%;u9v_Z>xI#f0FJL=@uV6m7Nwl^hK2}J03P1jttcdSEDm~qP2HKcf7ni znMy==ZjJ7GSx>w-*0(MF>i$;^ymGKA^NMKQOAB6JAX=?rlB^n_k~SU-stzd?1{)9MgG{(scac>OJh&k8SlJxM744# zi-~F-Q5F={I%2_1VhW)=n;~dJe&iGV<yRS(q``hG-LaO?cr5Cf>q?TZaz^|1h7^O{S z-AU|x2kCG8?x}2p#7p~B`rlRd0#;E^=qL03SY;l{&N!Rk>yn z{9f?V*XXOoL+-FcQMa!*ov>O#%^`BiZe{F-o^Iqlm3*rLFLLGt8{4p7?VO>DN&r3~ z8~)UOXllWBZpcJlD|~AnIh8GPfaYldc7k67Un_Xu_gcBT_iZ8TVWhKnKD56|Rq*b7 zNJ&!iu|#@Pk8(GjAj~6mQgRz}X7KYm9}{-pdQ5!ngtS7nG>JIeZX#b+kWt2`5jpLT zaTg!o^VPGUr9C>JOG9@|5N5v!OFE1U_xk%=H(kWIb>7glGyFXnQ{&y=byDAPs(YrK-fuhZ&VYpJA|iW5YUq^oGsu~m zJ(cI2#7@6n;^BQi!Z!oo9DGfDr`+~@PQ2T#TkmbFjd@iiRH_|{U|#n_`k^39zaYaq zZNEsaSH~t){-l1Zps#>F6;brthSm+A1AU@Oh5I5Cf*ZLRP|O9KOTw$#?t6_bKpW;EOC0DEN8sBj6>Q_L2NA zf$s!g%Q#E=))x=%3uk3QXFq4#yQ;6MmoQfU%&V`!KEaezD|~?hOFmklKLUMj{yM;q z<-upbkAM&6TjY#`@64eu13v}69sB{tA$%pf&&GYkHXvvz_ab!Dzu+!SzEked{;VE$ z`m5bGChzfwy*=RBt|C>8ZwZ2;U$>64SiH?5Q?{W;e0VPC+)Yl?%rnw8XLGo#yN?_4e zKEFDZ?GRi}f8cb9+V>C|%phy#lZ0sXCf~17VEa{M9i9pQG2OWoEDy20wM4pp_xe~e!v_1TD=@htO9Ehl7O z;yr8KUBaAGp~9DfufpMOK*^M^pE1$Tcu%B051T5_6ny*P+a`SGk^Vjp{xEpir;a?& zPf-VI#967;8~K0FFN~))QXge^P?jin4r;wS=_uDzIy~Ff63X!ur*pgbp>9A(Q29ho z3v%Yq;SNMyA0wf)=7cOHRm*~Nb$?Mqo}3_nNA!0bIb-X%lTl&xAE~z~gRj)`ZNFCa zmXHA{XS^5AWAGxnUG&X@SUvIeftxm8Q8IR<}c5qDpP`PIQMi7M#r_U>EOKjyjo38j#6Oc;68 zyMMfyd}Cwi)c8{Cw`J6oh%C zeOkaD0KZRgzF(4v*5XF`v)7$`_0DHXy^Zqc2zCFaB;9%V zPh3Fz2GW%+LuX630@~77;%8p?j6J8T<_Y*GHP(*k$`zq{T&#n>68gPb|7|3G>6p1g}UF z{OaRXcD4N@{f(EJ_RwfGzL&g;`7HQa!G+F01kUFg`Z8b=`t}@p?T_y??xxm@7U)Kx zlXJ)R5q;?Z-*MS9+V3i4p3_*ncN>_65|SMAU5#aE%n!r=2K0?DZSv_Kbm9&{<_ zPLW@myJg5!~Nk8VS+o7Af z^5?(8;BgzhN6M81-#~eD@r~eH!K->V975j;ek6xp>c12G1o$qY_4SALd_!*at9m{P z{pJ^$@kw}iME(r;syz4w@O!~aTJ{n82pMe2!3#|(_;&CuLLAD^Yggn)=C2Em27To<#(szN*pK@N-3)Y9&x39Wx?1Rjhw@pwmke}!)ql#WnZG^sIPV8Tc*WbM?Cl{N6nD4d7eAtMSUDA^B+mKMB4vC*2P4b2)OP z>>2Qj;75htZ=ZYZxDvb|MC4CFzpos>Ka z_6xs@E{sDj^}0s-yYV}da+ktdbte2ZxE8*<|Aw%mA?Yo`e+>R@e1|{aKL_rYk>7ZP zzHBG{D)g%Vhv-wz4x5sq*7Oa~&q0rQ&5^%y{-DYa{UY>rE9g^>>VpCQS=wbB`r?YP zUWuG3@I~O|yRHA=d`-`c4oNpvL=ydx}er^N8vjJ-{!Rl;ocW} zeo~H2#w8l1_C&hl{v6*_*oXbqA7f}aeck$plYUXTX(Uh;h|=+o-S}sf-n_}ikxYE}9;!L< zu%7!T3*%zO1N~X@(*S?TOX!~$@DKMsdFw{9;#({0MrtwG?ttP-0xBgiiMKO&$&c8= zQRGcl=c~^>p8qa-HwE1Sbj8AG9_fSg;OD`gEZ`qMp6JKB51I@Zwc&)@imR5Soht6c zN8Lfc3)i!a!`vUL%=y+RKP?8m#CzCWAGn30hBYHXs zz8$>m@1=ZJfAl(z%){RiE}!s9J1t3h?nLjRicjyckcq8qu-GmeEacLT8o9kg-Q4k| zv$T`QtJ{O!yZf2!tnR0NyL;<}YS&v*_7|ze|Ir+QffWUjmqA|38;JMmcJt$<5{y*A zNp(QSp3QOpX?1aG-D3fH<8MNbTvc9GPmDA1em5?m!)mE2i6ee25J0rLct62H39PC6 zJB%?wimJyXj>yFVdOYmiZ6$lYw%ZNscHdV)B6mYgpxA1iBZa&Z&Aih`67cmk zV0Y{hOfO2h%UU|?QHj3JBIn?J-hABlV-*UPSf<1=ZAh39Szweg_U|=}CvV4p)_P_6 zy?*oFm1?E*yaB$BccPDheu7#Vd-Ooa9tC59Qe4sJ6#NI@#W)ri-&MSYw!tWd@3Rvo z9@YQhYil9CtWoN(>i^*PgO_tY)aQzE=Y8gYy{P~jD-P9%=wroQpaP#SCi_WHs_>L*6mu&GDUlDS3=9YW+7TFDdnGa-&+_d{-W|&2&Jvh-=_8 zi~QR6@~$R9m~yLd20PArT(xv#7V+YRJ3_6Zs=)k~C=9ldv`^*T#G{dWO1F=;8+0Uf z;ZMA0L%x7v1$^=@Q38_G-F7yWF{A zo-TT_2>nsymWV!>NAN{d+K~@1-;jQ69%=V7@UsWdD_!1z{FI}jS|;CbmQHAF1D>S+ z-F`)uq|=JL^heD4f=Nf}rxSby@6@XmoUf;9e33R#$ryT(a}@d{^pyrDK=3o*oATfn zz_;b#g*WnA@&jJgM<`t!D64qy_lGD+N7|_!n}lxXl0?&jUU}!q!Ku(EY<44%-h&P3gxx>9=(-U(xNS^oV|N zpUvlP{Y3@JrCcSiV;=oU6Cd!))nw^KuGDiCbZb7fa(>zXUfvNXHrPHw-vYiCywtgU z1m6L^IS+jX{J|W&@Q#BY1K%J*LjEPgfUT~WZ)=21^0ENmG5G2NdaUhDOMuUB=N(CJ7SDljlD$B1v_*o`Fk*>5pF%)vK;Uz%clMEn=?h@4jNozu@`Qx*x7@t_m@ zy6+Qz)_%k`P6WsuqtQEEz498Zi$r%96uin=ueWF3skaxZy@hgX=bKqXT)QL24#TxO z*Y1j~ty{a>9vfHV>H_lH9`XFwc6*;`Om@B|@1FZEq#QoS>^jl@*Cgr8@J>VH*ZJjA2Y9{HusiG>Xm_7QqojRG z-hjNv!|Nqt7b?KdgHK8s{qe|CGeHY~19WSiSQ*D@0bc}us}Pz;=sUnyfDg`Fq@QHK zmw}%bTED*q{J^xMd;{G(jIzItkte}~7FRXkr>g#syd%hK(DHKppq-&+lokz4Yn5MG z@kZuY@Nd@bY3Jd2_xFS^akcw+VujLQTahz58}>_-e}`Y9=7Odql1-`WQRu69N9BSb z*m#TvOspF^=nOxo^r)x9>HP#78b9TvKI!Z;Z7U*#aL%ZTq`QEeIpmzwazf*{iai9< zmHH~BQ;z(Ic(yLDe+E?Zc3P2B4`pwH{%2<0IuL)6{XmJnwGx5xwsGc6Bh>t;8UCa5jQ_eGCcJon zcfLeI0SP-T3~YW0t@KN-l^q*LUiuf_KAK#6wCX;s1^D*=JNeM|bwom0g+qs(;jd~v z8gqKT;w1XM?4sh1Y`7S{8Q z)d>H{-OKh&Q~rx`Rp=nBWoC0F>{8C7$g4R2wDu#7KSKV!{pygY{eAP$rJ*ZzLCE;Y zoiPDFOgmfE+7HwIm+{85hvwQnc_F)(S;S3qyde((6`8JHgR|VgJOL*6+;9dYd@$ z_^fD6&xP(zdp1;@FpP`jc?`aajlAPk^Kl~A=aa6b?#6KUa4yK&HpMErQ+m4qf6Ha3 zt^XRTFV)7Tx;$?lKB?!j`zR-T%5U}CRmG`t*S|#18=!B8epKf($IryR`o5)XE|>r8 zlszd^2`@-y(jP_M%;mhRm;bF^>3M6)QK4g%Gd(-pxG33r($-9Fkp4rDS84Ag_)_p@KcDr-0`@TNs7|DenQiL8s*j(HYQGfnHebE6pN@e~ zf=`mB)dS@gab9LM{gr+?4_`fe(l)t#>a8Fq{h;29zLf5xzrc4;>#^64whi^Qt+F0w zoB^*LSqf6`>X7U8>?|x`&OJJ9pF2pW^F^n#P1=rGf3aHpg%#nDcl*hO)bHJP1wdUd z9=Op@3a$I`0_k+T__U5ch59k;PXSvg^wQp??;uPAeM%7Kk@~CzAGwxx(Dh>5+gle< z8^XiGrkV$|z*l@7(1AE_FdUA#)YJu*zVESV0uv^sjoWdx84x$ zA8p{9!6(5V(RRmcU%RzA*v|SjHF-vT>-&ZVVVrmSmKb}Jqh5=(W<#~(Jn6Sp@Gf)m zVjr>VOW<3TfuL(NTA?H z!Eeh$e-wN@_$@i~GvN1u9}VCo4-4QI!An`g?Op3RSCU@IyZLj=%JNizUxHs~>?7Zk z;7{ejH-eY+f^kvFM=SV}TTkoxk$+CFF6f_&+zfPkZ$GWi0ht7ae;oV~@beM=iTmpb z^#K9GKMP&!R_aIWgLwqM2)^^>r?Z(He9?QbPp>fb6QC~xUjbft?IYi-z?bL2H-In8 zgKq)98N6iMJ{5fF0AB>YNsu}HP>q|ux+#4ahrSAWu`Twg;>#5H5%BA@J+yXN&1b4* z!M9pgeDC!KkbvA~_>10)-c;dVNCt_ro}1XvwSMS-Im3S}@sW7Xu+ux_#QQQ%deFJE zd&Eh7(Mk3XI0OC8VA{#_IqqkD(f-(U_*`NG>ukWb649-sGq;WW>wJd%P~`!ceC-R^ zkvjOs;46!tw)d#p@`e12p#3yr0%04!z#?x6d8O5-?fEoMp3ML3`uImFk~m}jaHfvy zX#?$xQ|(f>ADdPa_FKwGry2YRc;i0@{9(V}30)_2hoS4>dsq(|L;Z6cx}xo2doJ}l z1wI1a?3b|qN=*C}_DQ*Ft=}85BoXZwMBYapcb?Y!DgE_7vtA!u{nhh`O6WVFZx;c9 zxOA}I?(v)=RY__d+5+Dcd|sSJU0KP1YW?q8alUPo-aC41M?5`@#0uY$kR1}+kM)uPisG)34^*5Q>_Bn1l9eO0sT-~Ytxa^sdg=UKXJo7;r=i6T?M`ce1i~% z`fXqnkKe>XrRHI+@EwG2To56?loO19%WGkD?df(JgD+B#-P3$ZU)4O7uB}290ezJG z%|d?!`nj+@Fn*f5O?qpNI;irNe1P@iyTW#=1bG$U>+;}};J1P=l6;v*>ZB2T1^CJc z|FF*I-C<}oviF=C?L*7UrZ^MaPH@cz(`~Ek>8~{(D(*3k+;y)D&jVCFxI4WHll;y? zR|Z`$UxHr*zY%=U@2CJ@)T;4~d=IxvAbugg@d$kt^hxB1?G4kXoSlKTlb%PxtkI!KsJTWeR!w?m2DOsl)393AI)rJId77i(Ero3I7cIt$VSLIzN8= zQ9_RiiIk`lW4=k3GVm$-5c>Uk>Q(b8`>Eo3*iU`v+3tw<;(8xiAKC#LE0p{gqcQEN+g=wQOnx@oMDGz`9$!und+I{GIzw+w-2A&Bfko4)d@HG)sFN zg>L2@VLw3dGvK$rlXjAFn5PuDAozFTPwDa%6JR44^N^A#?O60-_P4hXAJFp@zur{b zcCbjQuUaCv>hAmS>TGV@R`St^yps2t`IE_q;9J2T1+VOw!8Zdt!5`d@UDWwg^CaqD zEy~MWsoGA4t6NDN6v8QJI@6~sagnEnS ztAcMDK9Uzc>p$7`L^UP!)LdwIB)_fj6}O(wE(#*Bp6G4mmO;UW&>05;)ui~x;h%)x z^l#745&ncYJHr1VUp<2S(k_eer#^T(yG^IB{7_?mD zf2^!`BAhD}RMOy)bX(w`g?~X1rhlpY>wTTQ(fy{)0_iqDItpLc$Go^vyG$s~9U5?k z`}IDOl#}je1IZVho?fT7hn)avC&g?eU?6nBO&AZGnTWLO5^^Vh?{xNbfBvC)An47?T7UD53Y!>Nqw|IU-bK= z&-dJZp!9g(%K8w#ariRub?JO7{jv4Px?p%gOl0#2-vWG#f57}A51-P!#+B(+!B_S% z;tw6C^?ft}yC|Ec6<@WO#(*0gXg}d^hJPCVGM%pOKWaTu_HnB#FQg0E(uR7xw9_d3 zhIFYVvm>!f;|zArfGKXL-EXpp=}!%ygjmvLqhc}G6w z_2({rJ$dfXuroZQC@RIvtFUjflEWnUFAu+r@Tf^gv({+ za)mi>fL&cd#eOUx?{KHLj^dB+GO)+{H>fu-CuOj2_9X>k)re5`xANoofuH^D$|e1z z75-WHPl-TZpVZ;4p0&zwd&7q4&nWbxpY!^EpkJx?o4-3W!0qNgyWf^sLkaxz@Sps= z@#`c_9;uHd@Rfh$#S?7%c?6#VKc5Fb27VfR$`S)B`AmYZOXF`l{Nu+jOq`x^Ab1|o@Cx4&eC_?-y02|F zGEa*?xeZw(_1pDc>yg<%1^vxza!DEwDaPj=e7V1aZoYH~+GJor$w4 z=fJ!C>mE5k7C8SU`D}o{V#pikt$)RO@ydKQK;HrV3FyuE5NZcCzNntm?^?HnUg-4| zj_y_KwKlWRCUwIj`ItrS_^{^}TYdK8%r$C%q#{>#t*8SY=w}3F+7nY zujUtO|D|cy*9YRLq8E#j-XGIG5mnBd_U$cj|8}JxAoW@CNp$*4#B+)iUm%|;eU2!g zN7Am1&^P_n>Fn_WMQ{B@wQ*9Ub7;*N$#1bBEPe@n>eobc9Nt^Ln^FX!$&W}DT&M~}xJlaa=%fDTbLitJBe z!~WUZ-{IxYzVFR$a5U9r7yZq@HQ~z7Sxl`${y6f}lU{t&ldsn$^;)M_e7VjrFdmk{ zzX*T*zlQCNn(q-myG!MQwu7$pLDGUw-rq`pm@C2Wx_IcgJN&3C>j8Z~aMRy+YX-mT zX1?QgKjNkyc6(;r-iO@&X?NgzuKO+DiSSng=1SOfSrSumjyB}CPKW1V2u^gnJ3PB5 z`Z@|-Cv;+E~=^?NE zRC;P0b6Jtli^Q?Qy?hPqua^E%2jAft;x0Npn@_b*CO1AS`qu`1`Vq>d`-k=8z5C3J z*>k^$u9@2wyxjygf)%udh*agCLGHq1p53$gU_9|Ig^Cp>!}dhJaz@8|eWU*s8*Qby4~m=;yGH5tY6zud%akk2H;E zXCRSB>{A1LTYt_xL-U2kbEVuyvF*9bpbjVDPr-lUKky5q{Nu-mYQp<&^qPU%?44&) z-T>$p? zH#5kKJk7etIS3E#6O&WiHR|GgIpHlE#^ivv`!{FFptMKXXW37?%zRJxPg{>_zNVT| zIcfbacjJ2{4c8hm0OYhFrz(q|qs!^no4SKvDo?!<&#Sn=4l#cKA;L>Ks>S0RD&YA4GLRJ2Wf}yR$FQencx?h@lNzLHRnVV{QDtmA5yDfcd|CWJU3Tl+Y zkVlE6?DF{>I$d-ov`!g}=sKVi-J{;E-BtF#o{XBVD8H)uXoLO`^yb`J zm$%PaS|%Y)q|V<j2YXnmb0ov96H^tm;w52CMW6Xp{n6m(BARBm!@A5t=$scaZ7UW zwhS*YxH-D(rex37-j^l1??}D8roVFF_IR|eqTsmd5zlp}E7DaQCHY6_*)?afQ`$aQ zKR+fRE)~bD(U%6xzC8D>!JL~&3UXw;=|s+S*%`aP%=!UpJs~N<>Fs^)Ud?X&HJ$z> zeA`}pCc8%46_q~yQ_VBHFqKfW5*W*zFlBW&gT~mp%ubvVYLcL3enCJT@tYhbdLx zZO}K}h#d*fUyXn9d(?&TE|4evehm8BiZlA2H{ZUJU)KC=z1{CSQtU&tSf!^6@R!|m z#@@4I?X&WKg6H_vWg5ms3+};?Vo5nGQ^bvsWA1?o>8T2-c-BGN-)88WZ$6VfqRZ*o z8+9H?jE-8^RQ4vJmNo;*zwGU@fy_S$4wf?n1 z-*MZS>^4CddOfcCez(XSg)Rf#zA&8{S98yuOL=FYpMl=^%{Je>_d*pp@A`3aSPf4cHaZoJUWlQ0Q)`SwO}>3&wy$NcUk zXYBg{R6Xc+VV>})jOQk!g!8;hE)0JuumS$PFK7G_9P<_m_%7LHAdmds%I}N(J}C(O zd&+q?Vj+=or}#a62l{IK_B;W3HVb{+E4S_=TE6hlz~51YT{PoC{*Cm*tMZqmnJc~O zOtwrK(E3YMzz*?{s;%qn&ikH?-*64EG{Ap6j{m9WFB9s#s~SkS`YKv`QFO%+FBI0oYMXhm?}=auL&Q`>r+ znuo7p$C=Q6QL*bw;E#Ye`#qGuK{4Gw2`mfFwGkK}9K;LZL2A$@*pK%Iq0mBPKHSZ2Eg^#llOi#m%<4_y=U)i-5 z2y2Db%Q5&*z6ZM`88wfz*ChBQ@QZrB@6C7PGQ&;CRJU4Y zzB1R9$#22MXPNJ|A+KbJ{`Dc+;XM8c+|MEtB6XNLv=1w=Dx(as^(Ndep%1b~5}udF z3Jc!o=G*y@H};mCQrP$%K~{TF++!;yww0vcIxP19 zdE>J6GqfM8qo~AZeLq(6o#ZnH-z0qH+OBwhq?xUXC#h~0aQpon2V)~v&LVO~KSO^E z=ppqR=-(g6)#f{mL{BY62dln_zu$Q#bS_i(>sGJQrM%719r~>2$JqOM-e7O#@h*(o zZZUei%fQ$Axii@&$+#c?thK{VttHP{f1H6Y1D}ak+4@!bpmr^qJ}Y9S9FY<3-|RY* zyN{Rl@Yf-#psRo`30-G6{%hl*?+;D5+_(GdF7Qe{w7}oh?di46mm1!PAbYOV_JGa` z-za>i;9Ju1ENj0^y1h=p-L7dKzhEWs&B8bFC(IN0ZXYS{;t26>@C}0W{d(+Sz1MD~ z{8akK*g@#(w4R6`=W^qwa zuAh#+`F@M^>kNFwhrD_H7IlV2;sAZF`ytKqsC9HCohj(&{_KpsZ)IDJ9Wk_jG~l>h zUP|<2RSs#_$p6CM9mC$~c5Qdm-IvzSlNg)|zx`8?nb4!+mvocD|J5`0y@g)%8i`@d%VEk91aU57e? z(ej+2abN?Bj31Fd!QLM^qt8>ZQ%c24ReaD69WrcG+jyz_3E#Yg45H*q(Nmf*2OkpqXS z$(r9CRK6CF)A)~Pvd3M72I7`6+47_|HcP-#MKR^F&a>W2EA3u2if{SPXR>pA5BH-K z`{LYH=b5eWbxwNo%-s2@OliHqM+u@wp+5$_q-h`NA4es9@a=*O_)DVp>^PF4&yyp( z3(!~oi#PA`;)^ncP;-esE)aUu+-+j&PSPp+Q|t(Q(>k3T`}59VGJ5?7`lf#k$7vW} zFdu>YiDb#lx=Q|6J^zEh>feZ$==8jNO1bQQ5WCyZ>rg_=BmHUy{&Dz&^JYjnFkfSN0w1FUmhYT|txM6G}R5(4G7N!43_P$N8oGM->MN{#4DYcY$wFL$(P}*}IdDF-<^W3mJC$^9_gO zr|b}8*rUv!wcSws_&Lhg63(Q}iVEJT6Px)LlaM9(X+>VwV`e^X;y}881kR1g{avPi z@Rl0&4OTTj9fyAk{$Sim`u7z0N$^H*H^=0~zPu{F_ zT*xU2UesXHk#a1MPDd7hI!9k>te(j6Tf1w)?-zm5L;H7?e=c@xN$*=qZ1CQXT{C!P zW>by47CZRHa(B{i=71=ZvpOk1^3nxM>izMQ-|C&-CvTi+CgfHH-LLu^HdY9fa?T)c zVeL|OLYFhgU+Hzc+d#Yq!(98`wyH7Fhtj{Gebz07^g-~I;2XgQ$7jLU8GMnFp#M|? zn!(pYFKtcvtUsXQKFK~;+yXgdO(u*t0h!3lz}E(!e7BE$9|wN`d@voU$0_hF;7{su zt8of*uwBL|8KPu(k}*_9P1o*h#?q{)L}W?6OTS8=SWmvSJezM`!75WQwLQxQe>^v+ z488{VwiYhgd#qJ|)Z?4~zBQ5G2Hjrhio)`%MZP|N-<*5AQq6nD;j1WG%8u*)?#;Kv zHgR7U?*kcht<7@fC`kMXfB%hXbJ3F} z>2STPy#sNne6;{!orD!{J`NdTk(WZ=fs&=rdLi|v_5;O5j9M?0R#Xm$I>)`5!=d~i zt<;4Aas zOa7WR2QM=1Bi}2)Z_a~Hf-lX3Zv?+F555(A33wUq>?86!!AJ7oN5L=BzPa>A!O!Qx z&w!uFgI@qY3BK7T3QY2ej1y-f-CFIh`0)i9592a8sx4^}Vw|CjF#N z$j4}4DxEPS+emVZ=Y4n3sXqoV!PdK<^FfT zbqWTaWe;s~m4qbUk*|^Oa_ob)U)G;zzTV)wPl0W#vifhF8=0_#{2YAE-@we!0^OC*SdSX58QaCj34F9!0hy52$`p>yB`>&kTUpj+db~4p zp-^RL!6IPpe5Fph{r?VspvAMBzP*=LN{F48mP$%XN#nggBr?#4$f}B)bs{hQ?j;+C z3Cv>~ymc9|ALG!?K__iuAHh$7uXs;*oK*fF_)_pDejc#zDg7RMV^(Avowo>O1WOua z-#|CtyJYt%`{R(D8&>CqC0wpf56j4;med1^Zv9*LzMSzXuOF1f$H;TOJk0Vdo z#LOR2U~e-*qR-tI^4=kne9XePuN{Bn9K|-n}*Tvg)3f_P(V5 zwt-s*w`6W9XgNzhM87Kjp19b9OW7vA+oy^zN$@AYoB6i&ztyg13AL-3I~%hDr}Djc zPaFIl@bCLW{NccSj%<481l3u2>G&TB^&q3Q;fqg=Gs642y(rS-wjtlT0>@ZP`jLOY zPwm96>HQA+{qj^W7MMz-b-bgc2)6EwcnUK$ot$k<+iKz$8q?Y2k_5}6km=V z-mYz~Hp6|&CTsf}*EYGJA^%tzw!_k{rQgIa{1W3w#Lu5Hoa9gK#}+@%o%82=zMG@` z>PGnMzr2)f)#I79IX=!hBsvsY2V8{y6!fE-J`@k&ibx5)tk642r{W*cyRUh1 z9ebYId%tgOz;73O(FlDT^u>~nc?91Iz7>3>MFK^BC-^S#2L+$I?#6o~g5?l7N1;Cf zy?nQi$eaN`4}MmVq5TbKUzaWVC*t;hgTChQnSH~r4W=*ot%80E`Y}zf>>hfl?`!H= z+Z3d)gQf-g+OIEVC$+wL^)3~T4Nv1P5s^_hJLuulj?28!*j^`X)LHopN0E2*?@T?K zI0EU{DmKXzkCbH|x?|8C*LrE!zp8CK(8dSFRo%6UQnDQ>U{L+jI-G3Ji%*hn<3Dpg z;hRg@MTdX z`8M-A!B8KX?pq0@EYO zljOT`l0QE;@kM-Pdk>uZ5B@r65|R$+x=t)*GZFqtY&hu--8_5~4Y|9!B6VZW4ZYX* zm8Y-kzjologJtfqQ2Y4xB>FXlocV>NY*PCjdLD+Hq)fqf$Ye}rU@{exS(y8)kf=QN ztn^=GorL~afN(p1Q28>hn0R6`mk@8vJ*zoW?bCw1;wPyOSCvzZ2Z(dies!q%aSFPw zr}5h$vyb%gG4Rt@EU$=rO@cp~2R{q`5cnc11SoPA!DsT|i~f~#!ROkmGVlj;@MXxU z0^bf^)B_aYc~96qbr53)1+_cY+R6Ebev+0-$(rPLcdo+Bl>@s-%s&9@lat7 z$IODSxN1YZh%gzwZ*U_KN$*CJb0tg(em_$rRkSK-TOK0E$3SoKZVFM&6( zltJ4J-wF6Ohxvln%`3g^gl-PHh|rlw$~Ovr3cP%`k9<+yyRm6 z{9*9&JzQ=s12BVz{8sXRXqV@plXR+}I|N-Yzrx=DKJ`5K+o0>p!=D1*_B{BHLU#Z< z={xqROS#so;}@Zv?&$ zzDFK~FQii`r`GE;=KXiy;@rhKi`gdVEv@p`=xe+meuZ9)LO+T8Z9;Dz$?s9{3*d|O z_!{yj)ZQl3*WShp0j+CR1>d5`yK33)msVxO4x4*%;)Pye2q-#htzhTr$=-))^Sc90&pG4!^qd|BOJ8+oEe()Q+*YAPwZ z)VKC+#;@_#q(z+YFTme;{j&1!BVUK#f48r9CseiV-*NjzxHW$TdfWwnJO4*M6XG|o z5RTDz%%G99UxZuR<(BW_zrZhTVjr=W9pIdA-16cDPv@s{tA6W=*=VeQt(Oe>nsu|_)74VdGK}M%ktox!I$L7uS8B8_%-0` z0p!!RrT0ErPXz;(PtqTUZzFs~!e<`APk}E2FW>DWIyDb|9r)+cucGf$7tl2evx!G3 zA8_q!JpVvsS3FJ}*~0H*{Jvkyw>~!ZE%4rM`He^FDuW&`K;K0_ifoUIy|S^@ zL^pdTWiOr7`y_H3ZZ`g`F!Kn07W^#uCPA1-+IA6q*)7X7X)+?^DUy>gBdf&o7P@);(Nf=X*+= zs`tu=(dU-Jpz z;l(4~XTQQPkH}d-&g^@br$zZEryuj)IX~mU3EP_kc-UI_R32yiZC%ceX*qf7=g!=H zi+AT9sI1OKxNpzJkPXnHz^cK7;tQ)Ee z2NX*Q68)G*PREn@I}&&H>&w&6KKB8$hUs+`m44|gI`Z_gy${%?@9ksjbCV_q9^bvX zg1uo+%{!ZsbMO@Nh_HSdKUGbJJpUm){F?p`fBiG~l@XQyId(O9`=JuD6-xF*$#y8N zGd=GrY7{1BVAyc~M(4a1!p%Jr058ALSQFQ;ud z+q!H7)FBkxkC7`+8J}k4Z+X^>liPWYxxP~O7oqEfZYy+E!syS3LUb}Dk3&}nU8&HS zNAOeN>%r&FOXk7Xf-kqkK%rj(zZd+r0ABQ^OLJ5W!3?a(ZFX0Ig{r;n;qBw$>B(RLsxX@uHo0nWMQy2 zvpvysXYcO5y7aDOcYSJ4|IUFO1+RCMyg1gu4~-s3x{DP|i5-3ch+v5f^Fyat1j~ z7d)Hyy$N@chhV>xc8vU#@ecY4$p|?j-HG#tjyuDTO1g>eN1W8dPS1?f`;gQ311J4` z=RU9QMUpV9Xf2*tn!rjvtR;)S zjv=@G!e{OI&I#`0K3OvKU3d69dOzcooBB4dn)sHw2uhDALenLEKt2obS6=jND9$ws zKJqi#75o9QTg-P#srOAoj~5I-Rv_)#^Miuk?-xXCzf}<3@&C2=^+9f3*M0ZCckr>_ z*bkB*xgtSvNiNAHO^Hj2q)3XgDC%n|G2%+4J5;rlmNwN;5<{(|r9|0~1Y58JCuj}H zkO*6d4JU|$W-uMJ1AFiXk{~V;!xC&kF`D8J%oJ_W1k=H^Xp7Vcx4-i~C=lM2M90Zr z`(_sRoO91T_q=n@z4zSn9HPt+?pHS}Zk@}K>w{_q^PYJI?ZKhFm3)nfRC z_G+>2e1DSG?jM@;8jCsW8H1j6=-B|wdJaHO$G5(weEX}~?~#AQqXb@8EcgCDX}Rz3 zro6_>Y|CO(OMS18mNXzm6E!|$_)i)H&gAHkU=zqyKUsK+CRr#h{2`3;epPd7@k8Xl;JGOD9MknQdI7h2oev+XDT8`kNSR~KgV_U3bilkMZceAD2&{3-Y@gD>$3e3OtD0bdP# zHxiF=yAs+(R$X22>-w7K7ybRLZ_?eIKYfUQ?*dan3ZA3j&pKfjef>1gk#}mp4ZvTxy!)e*B>r(BXVf|;rZ(nqAuR2K;|Z_220* zkNUi%8izDZ#0bmy8{{a_-rW1Sg6%yGx*zl-O!z3@heSA9X z@Rq!!kBQbL_csat)aL8s2$nMidK7f-8~1Il)7Spb)HS71&Gx?x{t);xdcW7_Iq!Pg z&RI_Fw+xgsoj>>|78a!aAe|SNF(($TY>=x$lA=kVXH8rkA?ILX>uU!;v1lVI|@e6XuBdI{br&MH=M4p^W zQK8E%a!W~4Q;~Uv;v}l9MNXYU^B56+ay(5S~g|k~mQV{pfwJk|8MFhB(@15-vlT?;6rkseY!mmWIAhT(z@ z>hQRqKZ>|fGmcjoGwtz7{=zoPOL8l+qMUB1qM%Ym74>%IAiFmu$=8R0$@OS`Z8=|t zKlv>|wQLs|_MW1h0Ja#+c2TuUl)FYX)}wQ92@$hW={AvJ3kPfzyiLSxIY{vIP~|oe z?xpH&qS%YYq25QH+eB?BHpb6Ts0{LLXaoD~kT*wQV@zR{@+1@NSrN-r#LD9qu!?Zo z=rRAV;Gdt`H()ckt4EPVWeWfGDXH>ggXP-&3}Ze|6-8oBplp4jWsLU50rPMl+90g18Ci1Tn@tX*IjhiUEPWaXl+UM4xQX8u~ z<^Ic5N4LER?i}1kaflvux19h7*s&w_iUeF?>m_=q&?j`$VN{zo5tj*AE~8S$CdxLwDrm*p82G9bAq$o9^RIO)HQ$iJ z+e?9)M4*>iqoSq3KGdlG1C$vOX@^?@k{d?f_2Fzc;G|2FTii&Q zyG3G5L-N}weYc2i*O2la3g0Obdo?6}ki2(_{J~5YD-%0Lb!d@2MpYD*V^lA_C9o#!Lq{*@+c#~ zJwKIEjUn+fh(w>E4|Imy2XY$m%!e@chf*XzlzJF7*yNB!Uko#pGS)flzn@~?f9W#+ z$W*?EVw2b{xozG-sYzb#aqbzR=w1<92{3+BUd` z@-#M5ks+#$izXGht8OZ8!;*_36Zu|>Y!#lqDCn(0@@^C9A?A;+r0`ZzT3G~-XEmj^ zi|lF+wo7Ztvkf~}3p_O!MYoFhh8*DHCh~0+^-T~{*}~#mTevbX&LU&u+$b~7VhZC3 z5vyCNI3e8Im?eczkhd|TZ#yNoi^z5sP~T3a3A_}=jZza-o)Coz7NhO)pBNuvb4JLE zd!p%Dk4Ram+>Irb8oi=v!IjNQk^2HDR$5FDe?BdR^jQS2pe zpQ!gT?BOk_)JLs8(dg5|v0JAxI6-;YZxvpZx2dpNQ-DM{d}>&!ZWBHp;!5UQiICxC=Q>|YV z`$4GmGoi&GM*RzMIfj##LZ!8$z{d!L3e~lUq37U<7HX_ToCm)xSgE*HRIF)?thbBG zYek|f1Q;=&i?D+V4~vAunS5A82AL;0=yX3U%0m=CAw9#+zzLBZnM5ln*E}v7E2(r`G@UFb zx|%7u)m)2+D}e%@;7s>dm~dK8@V>Qi9E+e zeiK!uMR^O?`r**WMSLq|r|`P0Gd3mM6HJd!FeST#(hrH!4rk;c(fSP2W4oA=xrG7` zisCKK(nG>K$@JtTQwn=1IxVVuoW5z1+Q;PqTDBjL+hP@Av=OGb(fZ^H$6nIE@rWkIWIFUao z%D+LGlV${|oj*VMEafQkq{4KrbctdY<`w*MWtHcDQJKL>+Dn83Lzs@}H5IPOE3+{Y z9-=IXkXcjYnv60VCecs{#|s}F)!QslbH%SpuUb*beTQhO&hP<|BZlGJvBM&2saSx7 zdYtb4qS(U~wVu(?VUdKR-yw2+&cb2g>t{IJ@ALw8Xt2ZSfkSI>-7%Uuj76F0g&}9@ zu<#Ev92p)h9_AeiaC)UP3Dd92s6c{ioi*5UEm!zQo%#KuFv@Us)S28bBJlk?kkWIy zZx<~M_OEj`593QBOfRl;)-i4y7*1_)whoK%7{iG%XLY{_Y+^XN$r;)&+?yE=Y<7k) zd$uy1+(ymYMQ)q3al7zOP!)GSkyKDo{0vnw(4TSU4~XEc%_j_ zG|80AUQN(#R5>hiw>is)MPwht!TlH&xO(e$QHR6Bx(BIzNHpQ_cZkd(Y911$Lkw%z z!jjF;%V21J~|-ir{Ws35nc z))gvRBobyYkvah|Llm`%5+9en7Otz~(`NG`HbA<;h+*);n9sHg^TdV##}?ti#EoKx z$XD`*c#fctqv!*{M$(4xhYA+i;=seNP5?|3MQozTeRwQfSHq{xrlkkr{2*X*wrs-7 zebj8^x7&Pntm9@M=l==%uxovsmD5K#1VV~4WLI$(Pg0KU!y@=R0zG z40nMC6(jronlFowD87 zy2k+*x+vT$YF*qw+b`1H490p+x1eh;WgXb9PaxF51jf#d35+u#Hf<=#AB6#GI7AGm zA_s44fDJ8q+%-Qm?3w824K>y;V%VyYRaNHPIJ^?od!>((Dv+#gJk^hVc^Yt}iwgZv zdmJ!wu_biV66 z?jkM^hSp+xr@~q+djY)3<{e@R)*{3f!<1Yrn*8n8s*^y?uBHYO>}#mJ21#`6sM_i| zrnyZE!G^>tE0QuV5t8^+7ebP_I#xsOTlK0SrEQeur*aO!8FulUmV2!j4^w7N;f`{$ zrE(~P#~%L<`6cTl5VWiQ5D7q=R|3->!}z+&?=n(L!l*w$zUl{B1e8b70$Tln#5~OX z5S3_H`5`tnFfVS0Cr&_<^MqgyDZ}W9NMNvr*d&n=9FX?`Mgkv!4MIS`6*f#1YxD|{ z9%h1fgyLAFM;I;vM!2TIffbZkA!1m(hq&V{v#??u&otK|j0voT48-~CMS5KYZj;|g zg)vdz$QHd4waa(y^XLWZMr5A7CNwXac*dD{5h2Pc?{C8r9OGchfr7M=*Mk&cA9-Jw z5+@j>QAJ^ps+V|EVTR#Tv3U_%&2V;2UCaLFT#Pk0oJt^%9oxo8ar>zhlB?lKM&f%Y z7emcnYW<#+^}UqjE)G%jk3l@FvEOkj8%DbDZbn+iPSyTUipCTp;RjD;zmJ6CG$Vya zDE&RKJxa-Ur40Nta(^57&eIh71LRL1V~)(fAm4Y9v3#8T-<8rcLmqI%&QdjktmWsK zBmad{nXe<)`3xiJXUX%jNL?WB%hLT@6n#lVk#GJI=blj;e39zV{zZmsFTzHFc?La~ z$oG;6A=^AJa!5U25|vAEkLpY0y)4`>bNkTC)A7r~bD1KSMdTGqe^I1f;ii!rB zH8EeJC^)}F5q!EOCEd6QlwfP32*L3FSCO$r*+dy+964>)1w|AlCiJ?T4l;^e z%7-%iWpE?YokR)l7K#v1B)^Hr#E;B&39gyllc07cU3!!A|dypNEG5))9 z9@Swa$D8i}MN&NXwv=U*b>zCMh<^$Q+Txt!Mtv3~i}jD9b2Hpt0{mC)`p7B~KRbAE9sHVU#>y zyqM=v#!-UZ_itbzRt;qprSFf?jB%8x*0-94EBocnnK3+bMBIvJku5y0!m~)ESS%9f zZBSAi9!IQGVI2Dq^4N@qT5R%|oPxa?ii#z)s^IA}Ke)#;Uo2B9l~o(kE)lYY`J|8` zJe9W9$lkKpa7rp$M9D&K!t+h?+c>f6(Ijh!5Xipw7A>xxJ%vf;lSj(EIGIvm4WHrw zH!UND)d;S`N(E17yJkR2;vwT2j-|2v^f6Dcuhs=heifxxiz){NL1#cRWi{aB8ggUP zTEmU7wBChb@*%Y>FW_EPPS|U2CkSPu+t&c>sMVKB!ktlFv1Bn z%p^P)i1ev>i;K?*HWw%0TV8eN2LH)O*yH#Z};Q6UAT^TZ$_X$zbr1 zeCtJ)`KW%Yqbz?xj-DGS##^4okSy+$sqqFGZOnk$|=W9@^wxmqltUYO8kE3Ko$=xix4lmQeUmNrv#i)d_Sq3&@CkBi_qay-RxYL1IKKB3rF%5Am9 zw>r_X33!{WwUttM#*&-%iyXBD{ca@yKJVr8!R3-4-YdX@#x5c(ApVGTSZX?T+RxmfQq2Zm|Sz zrOIxL!Yc`{CF$+hckqXKHgy`qjnymNhtQvr$Y>}H6)BI9)fL54EbEw?^Bx|~g5W_A zR$?580hf4X;==}(=6*`|<2*dZb7_NId!AoOE6Co4lvPfC)UlqPE{(WC;(*@92ELo*%F`?_BYO)Ht1H7LiF-6f>3yE2qLglVZ&u@h*|1qM zW1FyrD}B;FLHpzp`?b~@ZQuBu`1pj*!zbiPn-OvbFUKwq7xYMo=oQaOM)@gm(#%t!8^bw<(&H=iBuF z?F3zr^X;@jX}8me9Mx&gZF={dcX9FCf>;wLgw^(?(y{rM~sr zG}Q^qpUW0lw!pFlmMySsfn^IUTVUA&%NAI+z_JCFEwF5XWeY4@VA%r87Wg$-pr=Dd zCyt!hb(ako|Cv{3eJ5@;8I6*0WXi5xo&7Q1UXO2`Tz4_vUf+FPtq_d2*Y|bQcj9<; z^}37k_Dme|n%7+{TT2jLM_n4}vaee~Ses$vQb^jRfV&l`^ zkNLYxCan`U=}VREwqPE{@jZ)cSGzAVv6H`@*Q{^XdFYxX9bmlOXWPe$IXdx9`8SF$ z=D)fo`e*0;Fy8J1?J&2vQM}V%OdMZ*x$f$`?@rugK4St#**V^wxM{B^I@+0fnB-2} zoR`m;3`W_xo*e6_H`_h61l=UxVgg2K&eLZ*=%zfAyi~hclc7VNNxsztjIz^CW;L%p zi>*JrW-UhlIR5r^&Xm8D9QLcl_?M!yU*D)ci}7=xi}kzIeX&0+#?Rw)Bl3A&SxnAS z{W9-T@)x7usGOzvKaReb{!8h(lpLPtT-SLm#n1C^v3{50zfpZ|RL;lAUrbNl8x~vJ zKCa)z_XDoc7 zM7zv$(9aEh%sAfc&)h@8FX{T2Yq;Au0FD^_8>m=i;C8F7fJtwDpjVjZB!>}ro97U- zKeIH%h+ix$Xqubja@D|thP(v>H}$L=_^F&O;qMH5z*O86{EuAv`*e+$%Vj&TtNl~^ z!OXlqU;T4>>3UHwv(M{g+$d)q`V4fvs?)Dc3!}Z;t<$SU{Sjl`G5P8SeV0+r82m*; z{^;lQ72w=ebEfMp?FQee|Dn@Mzp1Yb7YsS}6}sOvO}WN>>@(Vhjq<#q|L7roC9}V# z>+2oS+aGc2Wzyht7<$c__iga|@6lIE_kLZ@ywTsiMn4gQ-@I?XfhWdw{>J~*@55op zbG@MJ;V|0!4g2Q}x+y39=XyK)N?p&Hf3L5=GyhgE#|?QGy7i1l+>jGD*SH?-@XyY4#R8o%ek zN4B~qckbUg@49u@Eqiv|a_er__=SsSFP=Sr_RMo9fyt z<{f`k>#`TI2b$Y$))+Xm9P1El#w#Xy9RT()v%ML|n5FhVn^14=_hvj{%3=AegW2AU zbIh{(C@OI=Kg%%Nn{mg4K{wl*ag$k^aT3_HtJ5#>b}`;1UwGDRZ^j{JdDxhqrv4_M zS>B2E9OIa{8RwYgjMnQf>u-<+zR#iEo7vusyUfyzyUhMO@Bg10?LTV>G~+n4G~+&Q z&oi{M{o|mqP0Tp6bKi5B0V#O+GUHaWoCU4DeSAo_uvuRCtQoOt&9AlBF(Lk?(cZj& z*CYD6W|q7D(&Svc{l$*FB~!2yUYWFA)r&58w{@`my`~2v`Y-dvNulTe%aZP`t{fyDx>tgLbx=g)I%3lLuj$J0f eD9!d;I_Rdf_R{RTo%Ic)|Nq=h1Z=i5$^ReF%zXO* diff --git a/ucrop/src/main/jniLibs/armeabi-v7a/libucrop.so b/ucrop/src/main/jniLibs/armeabi-v7a/libucrop.so deleted file mode 100755 index b6172412c92bf6058eec4f11377c2bb3d82b48ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525836 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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()). - static 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 *const message = new char[16384]; - std::va_list ap; - va_start(ap,format); - cimg_vsnprintf(message,16384,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 - delete[] message; - } - } - - // 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) { - cimg::unused(module_name); -#ifdef cimg_no_system_calls - return -1; -#else -#if cimg_OS==1 - const unsigned int l = (unsigned int)std::strlen(command); - if (l) { - char *const ncommand = new char[l + 16]; - std::strncpy(ncommand,command,l); - std::strcpy(ncommand + l," 2> /dev/null"); // Make command silent. - const int out_val = std::system(ncommand); - delete[] ncommand; - return out_val; - } else return -1; -#elif 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 return std::system(command); -#endif -#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; - // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. - std::memcpy(&u,&f,sizeof(float)); - 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. - // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. - std::memcpy(&f,&v,sizeof(float)); - 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; - GetLocalTime(&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 = 0xB16B00B5; - cimg::mutex(4); - if (set_seed) next = (unsigned long)seed; - next = next*1103515245 + 12345U; - cimg::mutex(4,0); - return (unsigned int)(next&0xFFFFFFU); - } - - 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(const double val_min, const double val_max) { - const double val = cimg::_rand()/16777215.; - return val_min + (val_max - val_min)*val; - } - -#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 uniformely distributed between [val_min,val_max]. - /** - **/ - inline double rand(const double val_min, const double val_max) { - const double val = (double)std::rand()/RAND_MAX; - return val_min + (val_max - val_min)*val; - } -#endif - - //! Return a random variable uniformely distributed between [0,val_max]. - /** - **/ - inline double rand(const double val_max=1) { - return cimg::rand(0,val_max); - } - - //! Return a random variable following a gaussian distribution and a standard deviation of 1. - /** - **/ - inline double grand() { - double x1, w; - do { - const double x2 = cimg::rand(-1,1); - x1 = cimg::rand(-1,1); - 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); - } - - inline double rol(const long 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); - } - - inline double ror(const long 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) { -#if defined(CHAR_MAX) && CHAR_MAX==255 - return x%m; -#else - return x>=0?x%m:(x%m?m + x%m:0); -#endif - } - 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 (int)(x%m); - } - inline int mod(const unsigned long x, const unsigned long m) { - return (long)(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); } - } - - //! Return sqrt(x^2 + y^2). - inline double hypot(const double x, const double y) { - double nx = cimg::abs(x), ny = cimg::abs(y), t; - if (nx0) { t/=nx; return nx*std::sqrt(1+t*t); } - return 0; - } - - //! 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; - return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; - } - - //! 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,(unsigned int)n); str[n] = 0; return true; } - return false; - } - - //! Replace reserved characters (for Windows filename) by another character. - /** - \param[in,out] str C-string to work with (modified at output). - \param[in] c Replacement character. - **/ - inline void strwindows_reserved(char *const str, const char c='_') { - for (char *s = str; *s; ++s) { - const char i = *s; - if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c; - } - } - - //! 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('a','\a'); - cimg_strunescape('b','\b'); - cimg_strunescape('e',0x1B); - cimg_strunescape('f','\f'); - cimg_strunescape('n','\n'); - cimg_strunescape('r','\r'); - cimg_strunescape('t','\t'); - cimg_strunescape('v','\v'); - 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' : - cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns; - *nd = (char)val; break; - case 'x' : - cimg_sscanf(++ns,"%x",&val); - while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; - *nd = (char)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); - - // Return string that identifies the running OS. - inline const char *stros() { -#if defined(linux) || defined(__linux) || defined(__linux__) - static const char *const str = "Linux"; -#elif defined(sun) || defined(__sun) - static const char *const str = "Sun OS"; -#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) - static const char *const str = "BSD"; -#elif defined(sgi) || defined(__sgi) - static const char *const str = "Irix"; -#elif defined(__MACOSX__) || defined(__APPLE__) - static const char *const str = "Mac OS"; -#elif defined(unix) || defined(__unix) || defined(__unix__) - static const char *const str = "Generic Unix"; -#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ - defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - static 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 s, const char separator=cimg_file_separator) { - const char *p = 0, *np = s; - while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; - return p; - } - - // Return a random filename. - inline const char* filenamerand() { - cimg::mutex(6); - static char randomid[9]; - cimg::srand(); - for (unsigned int k = 0; k<8; ++k) { - const int v = (int)cimg::rand(65535)%3; - randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): - (v==1?('a' + ((int)cimg::rand(65535)%26)):('A' + ((int)cimg::rand(65535)%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); - delete[] 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; - } - - //! Check if a path is a directory. - /** - \param path Specified path to test. - **/ - inline bool is_directory(const char *const path) { - if (!path || !*path) return false; -#if cimg_OS==1 - struct stat st_buf; - return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); -#elif cimg_OS==2 - const unsigned int res = (unsigned int)GetFileAttributesA(path); - return res==INVALID_FILE_ATTRIBUTES?false:(res&16); -#endif - } - - //! Check if a path is a file. - /** - \param path Specified path to test. - **/ - inline bool is_file(const char *const path) { - if (!path || !*path) return false; - std::FILE *const file = std::fopen(path,"rb"); - if (!file) return false; - std::fclose(file); - return !is_directory(path); - } - - //! Get last write time of a given file or directory. - /** - \param path Specified path to get attributes from. - \param attr Type of requested time attribute. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - \return -1 if requested attribute could not be read. - **/ - inline int fdate(const char *const path, const unsigned int attr) { - int res = -1; - if (!path || !*path || attr>6) return -1; - cimg::mutex(6); -#if cimg_OS==2 - HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); - if (file!=INVALID_HANDLE_VALUE) { - FILETIME _ft; - SYSTEMTIME ft; - if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) - res = (int)(attr==0?ft.wYear:attr==1?ft.wMonth:attr==2?ft.wDay:attr==3?ft.wDayOfWeek: - attr==4?ft.wHour:attr==5?ft.wMinute:ft.wSecond); - CloseHandle(file); - } -#else - struct stat st_buf; - if (!stat(path,&st_buf)) { - const time_t _ft = st_buf.st_mtime; - const struct tm& ft = *std::localtime(&_ft); - res = (int)(attr==0?ft.tm_year + 1900:attr==1?ft.tm_mon + 1:attr==2?ft.tm_mday:attr==3?ft.tm_wday: - attr==4?ft.tm_hour:attr==5?ft.tm_min:ft.tm_sec); - } -#endif - cimg::mutex(6,0); - return res; - } - - //! Get current local time. - /** - \param attr Type of requested time attribute. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - **/ - inline int date(const unsigned int attr) { - int res; - cimg::mutex(6); -#if cimg_OS==2 - SYSTEMTIME st; - GetLocalTime(&st); - res = (int)(attr==0?st.wYear:attr==1?st.wMonth:attr==2?st.wDay:attr==3?st.wDayOfWeek: - attr==4?st.wHour:attr==5?st.wMinute:st.wSecond); -#else - time_t _st; - std::time(&_st); - struct tm *st = std::localtime(&_st); - res = (int)(attr==0?st->tm_year + 1900:attr==1?st->tm_mon + 1:attr==2?st->tm_mday:attr==3?st->tm_wday: - attr==4?st->tm_hour:attr==5?st->tm_min:st->tm_sec); -#endif - cimg::mutex(6,0); - return res; - } - - // Get/set path to store temporary files. - inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the Program Files/ directory (Windows only). -#if cimg_OS==2 - inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false); -#endif - - // Get/set path to the ImageMagick's \c convert binary. - inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the GraphicsMagick's \c gm binary. - inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the XMedcon's \c medcon binary. - inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the FFMPEG's \c ffmpeg binary. - inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c gzip binary. - inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c gunzip binary. - inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c dcraw binary. - inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c wget binary. - inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c curl binary. - inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false); - - //! Split filename into two C-strings \c body and \c extension. - /** - filename and body must not overlap! - **/ - 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) { if (l) 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 *const format = new char[1024], *const body = new char[1024]; - const char *const ext = cimg::split_filename(filename,body); - if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits); - else cimg_snprintf(format,1024,"%%s_%%.%ud",digits); - cimg_sprintf(str,format,body,number,ext); - delete[] format; delete[] body; - return str; - } - - //! Read data from file. - /** - \param[out] ptr Pointer to memory buffer that will contain the binary data read from file. - \param nmemb Number of elements to read. - \param stream File to read data from. - \return Number of read elements. - \note Same as std::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 || !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); - if (!nmemb) return 0; - 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 (int)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) 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 (int)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::fempty(): Specified filename is (null)."); - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - if (!file) cimg::fclose(nfile); - } - - // Try to guess format from an image file. - inline const char *ftype(std::FILE *const file, const char *const filename); - - // Load file from network as a local temporary file. - inline char *load_network(const char *const url, char *const filename_local, - const unsigned int timeout=0, const bool try_fallback=false, - const char *const referer=0); - - //! 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); - - char *const tmp = new char[1024]; - cimg_snprintf(tmp,1024,"\"%.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,1024,"\"%.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,1024,"\"%.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,1024,"\"%.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"); - delete[] tmp; - } - - // 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) - _cimg_create_ext_operators(long 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,false).fill(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 the creation of 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; - unsigned int _window_width, _window_height, _button, *_keys, *_released_keys; - int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; - 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(); - delete[] _keys; - delete[] _released_keys; - } - - //! 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), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _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), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _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), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _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), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _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), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _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(); - } - - //! Return a reference to an empty display \const. - static const CImgDisplay& const_empty() { - static const CImgDisplay _empty; - return _empty; - } - -#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 = (unsigned int)CImgDisplay::screen_width(), - sh = (unsigned int)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) { - static bool f = false; - f = false; -#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 f; - } - - //! 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 + 128 - 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?_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<128?_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<128?_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) { - if (list._width==1) { - const CImg& img = list[0]; - if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); - } - CImgList::ucharT> visu(list._width); - cimglist_for(list,l) { - const CImg& img = list._data[l]; - img.__get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2, - (img._depth - 1)/2).move_to(visu[l]); - } - visu.get_append(axis,align).display(*this); - return *this; - } - -#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==1U?1U:button==2U?2U:button==3U?4U:0U; - 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,128*sizeof(unsigned int)); - std::memset((void*)_released_keys,0,128*sizeof(unsigned int)); - _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = - _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = - _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = - _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = - _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = - _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL = - _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN = - _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = - _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = - _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = - _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL = - _is_keyPADDIV = false; - _is_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,127*sizeof(unsigned int)); - *_keys = keycode; - if (*_released_keys) { - std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); - *_released_keys = 0; - } - } else { - if (*_keys) { - std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); - *_keys = 0; - } - if (*_released_keys) - std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*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; - pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex); - pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); - pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex); - } - - 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 *arg) { // 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); - if (!arg) for ( ; ; ) { - cimg_lock_display(); - 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); - cimg_unlock_display(); - pthread_testcancel(); - cimg::sleep(8); - } - return 0; - } - - void _set_colormap(Colormap& _colormap, const unsigned int dim) { - XColor *const colormap = new XColor[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); - delete[] colormap; - } - - 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 = (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 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); - - cimg_lock_display(); - cimg::X11_attr().events_thread = new pthread_t; - pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0); - } else cimg_lock_display(); - - // 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(); } - cimg_unlock_display(); - cimg::mutex(14,0); - } - - CImgDisplay& assign() { - if (is_empty()) return flush(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - - // Remove display window from event thread list. - unsigned int i; - for (i = 0; ishmaddr); - shmctl(_shminfo->shmid,IPC_RMID,0); - delete _shminfo; - _shminfo = 0; - } else -#endif - XDestroyImage(_image); - _data = 0; _image = 0; - if (cimg::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(); - - cimg_unlock_display(); - 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; - if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { - show(); - cimg_lock_display(); - 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; - cimg_unlock_display(); - } - _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 = (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; - cimg_lock_display(); - if (_is_fullscreen) _init_fullscreen(); - _map_window(); - _is_closed = false; - cimg_unlock_display(); - return paint(); - } - - CImgDisplay& close() { - if (is_empty() || _is_closed) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - if (_is_fullscreen) _desinit_fullscreen(); - XUnmapWindow(dpy,_window); - _window_x = _window_y = -1; - _is_closed = true; - cimg_unlock_display(); - return *this; - } - - CImgDisplay& move(const int posx, const int posy) { - if (is_empty()) return *this; - if (_window_x!=posx || _window_y!=posy) { - show(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XMoveWindow(dpy,_window,posx,posy); - _window_x = posx; _window_y = posy; - cimg_unlock_display(); - } - _is_moved = false; - return paint(); - } - - CImgDisplay& show_mouse() { - if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XUndefineCursor(dpy,_window); - cimg_unlock_display(); - return *this; - } - - CImgDisplay& hide_mouse() { - if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - static 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); - cimg_unlock_display(); - 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; - cimg_lock_display(); - XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); - _mouse_x = posx; _mouse_y = posy; - _is_moved = false; - XSync(dpy,0); - cimg_unlock_display(); - return *this; - } - - CImgDisplay& set_title(const char *const format, ...) { - if (is_empty()) return *this; - char *const tmp = new char[1024]; - va_list ap; - va_start(ap, format); - cimg_vsnprintf(tmp,1024,format,ap); - va_end(ap); - if (!std::strcmp(_title,tmp)) { delete[] 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)); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XStoreName(dpy,_window,tmp); - cimg_unlock_display(); - delete[] 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(false); - } - - CImgDisplay& paint(const bool wait_expose=true) { - if (is_empty()) return *this; - cimg_lock_display(); - _paint(wait_expose); - cimg_unlock_display(); - 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)); - } - - 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); - cimg_lock_display(); - - 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], - *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; - } - } - } - } - cimg_unlock_display(); - 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 (int)mode.dmPelsWidth; - } - - static int screen_height() { - DEVMODE mode; - mode.dmSize = sizeof(DEVMODE); - mode.dmDriverExtra = 0; - EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); - return (int)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(); - cimg::mutex(15); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); - cimg::mutex(15,0); - break; - case WM_ERASEBKGND : - // return 0; - 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); - cimg::mutex(15); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); - cimg::mutex(15,0); - } break; - case WM_MOUSELEAVE : { - disp->_mouse_x = disp->_mouse_y = -1; - disp->_is_mouse_tracked = false; - cimg::mutex(15); - while (ShowCursor(TRUE)<0); - cimg::mutex(15,0); - } 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); - } - 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 = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), - border2 = (int)(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 = (unsigned int)screen_width(), - sy = (unsigned int)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 = (LONG)_width - 1; rect.bottom = (LONG)_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 - _width)/2), - border2 = (int)(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 = (unsigned int)screen_width(), - sy = (unsigned int)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 (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { - if (_window_width!=dimx || _window_height!=dimy) { - RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)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 = (LONG)dimx; - _bmi.bmiHeader.biHeight = -(int)dimy; - _width = dimx; - _height = dimy; - } - _window_width = dimx; _window_height = dimy; - show(); - } - _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); - if (odata) { - 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 (_window_x!=posx || _window_y!=posy) { - if (!_is_fullscreen) { - RECT rect; - rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 -_width)/2), - border2 = (int)(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; - show(); - } - _is_moved = false; - return *this; - } - - CImgDisplay& show_mouse() { - if (is_empty()) return *this; - _is_cursor_visible = true; - return *this; - } - - CImgDisplay& hide_mouse() { - if (is_empty()) return *this; - _is_cursor_visible = false; - return *this; - } - - CImgDisplay& set_mouse(const int posx, const int posy) { - if (is_empty() || _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 *const tmp = new char[1024]; - va_list ap; - va_start(ap, format); - cimg_vsnprintf(tmp,1024,format,ap); - va_end(ap); - if (!std::strcmp(_title,tmp)) { delete[] 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); - delete[] 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++) = (unsigned int)((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++), - G = (unsigned char)*(data2++); - *(ptrd++) = (unsigned int)((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++), - G = (unsigned char)*(data2++), - B = (unsigned char)*(data3++); - *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); - } - } - } - } 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++) = (unsigned int)((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++) = (unsigned int)((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++) = (unsigned int)((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); constructs a 256x256 color image - from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - - 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()(): Read 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; -#if cimg_OS==2 - typedef typename cimg::last::type uptrT; // Unsigned integer type that can store a pointer. - typedef typename cimg::last::type ptrT; // Signed integer type that can store a pointer. -#else - typedef typename cimg::last::type uptrT; - typedef typename cimg::last::type ptrT; -#endif - - //@} - //--------------------------- - // - //! \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); - } - -#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 - //! Construct image with specified size and initialize pixel values from an initializer list 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 initializer list 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, value1, ... } Initialization list - \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 a sequence of specified integer values. - \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 - **/ - template - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const std::initializer_list values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { -#define _cimg_constructor_cpp11(repeat_values) \ - auto it = values.begin(); \ - unsigned long siz = size(); \ - if (repeat_values) for (T *ptrd = _data; siz--; ) { \ - *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \ - else { siz = cimg::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); } - assign(size_x,size_y,size_z,size_c); - _cimg_constructor_cpp11(repeat_values); - } - - template - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, - std::initializer_list values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x,size_y,size_z); - _cimg_constructor_cpp11(repeat_values); - } - - template - CImg(const unsigned int size_x, const unsigned int size_y, - std::initializer_list values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x,size_y); - _cimg_constructor_cpp11(repeat_values); - } - - template - CImg(const unsigned int size_x, - std::initializer_list values, - const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x); - _cimg_constructor_cpp11(repeat_values); - } - - //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers. - /** - Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1, - with pixels of type \c T, and initialize pixel - values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is - given by the size of the initializer list. - \param { value0, value1, ... } Initialization list - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1, - but it also fills the pixel buffer with a sequence of specified integer values. - \par Example - \code - const CImg img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values. - img.resize(150,150).display(); - \endcode - \image html ref_constructor1.jpg - **/ - template - CImg(const std::initializer_list values): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(values.size(),1,1,1); - auto it = values.begin(); - unsigned long siz = _width; - for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); - } - - template - CImg & operator=(std::initializer_list values) { - _cimg_constructor_cpp11(siz>values.size()); - return *this; - } -#endif - - //! 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); - } - - // Constructor and assignment operator for rvalue references (c++11). - // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! -#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 - CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - swap(img); - } - CImg& operator=(CImg&& img) { - if (_is_shared) return assign(img); - return img.swap(*this); - } -#endif - - //! 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) 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; - CImg item(256); - for (const char *s = dimensions; *s && k<4; ++k) { - if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item); - if (*s) { - unsigned int val = 0; char sep = 0; - if (cimg_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,_height,img._height,_depth,img._depth,_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(); - } - - //! Return a reference to an empty image \const. - static const CImg& const_empty() { - static const CImg _empty; - return _empty; - } - - //@} - //------------------------------------------ - // - //! \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 (%d,%d,%d,%d) [offset=%u].", - cimg_instance, - (int)x,(int)y,(int)z,(int)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,true,0,0,"operator=",0); - } 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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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) { - return *this+=(+*this)._fill(expression,true,true,0,0,"operator+=",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 periodically. 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++() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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 - information 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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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) { - return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",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--() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=262144) -#endif - cimg_rof(*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) { - return mul((+*this)._fill(expression,true,true,0,0,"operator*=",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); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024) - cimg_forXY(res,i,j) { - _cimg_Ttdouble 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) { - _cimg_Ttdouble 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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return div((+*this)._fill(expression,true,true,0,0,"operator/=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=16384) -#endif - cimg_rof(*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) { - return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*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) { - return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*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) { - return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",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 { - return *this==(+*this)._fill(expression,true,true,0,0,"operator==",this); - } - - //! 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 a 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. - const T& at(const int offset) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "at(): Empty instance.", - cimg_instance); - return _at(offset); - } - - const 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. - 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); - } - - const 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. - 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); - } - - const 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. - 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); - } - - const 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. - 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); - } - - const 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,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,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,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 XYZ-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 XYZ-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-coordinates. - /** - Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that - the value is spread amongst several neighbors if the pixel coordinates are float-valued. - \param value Pixel value to set. - \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 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 - - Calling this method with out-of-bounds coordinates does nothing. - **/ - CImg& set_linear_atX(const T& value, const float fx, const int y=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; - const float - dx = fx - x; - if (y>=0 && y=0 && z=0 && c=0 && x=0 && nx& 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. - \param format For float-values, tell the printf format used to generate the ascii representation of the numbers. - (or \c 0 for default representation). - \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 char *const format=0) const { - if (is_empty()) return CImg::string(""); - CImgList items; - CImg s_item(256); *s_item = 0; - const T *ptrs = _data; - unsigned int string_size = 0; - const char *const _format = format?format:cimg::type::format(); - - for (unsigned long off = 0, siz = (unsigned int)size(); off::format(*(ptrs++))); - CImg item(s_item._data,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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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) cimg_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 expression evaluator. - struct _cimg_math_parser { - CImg mem; - CImg memtype; - CImgList _code, &code; - CImg opcode; - const CImg *p_code_begin, *p_code_end, *p_code; - - CImg expr, pexpr; - const CImg& imgin; - const CImgList& listin; - CImg &imgout; - CImgList& listout; - - CImg _img_stats, &img_stats; - CImgList _list_stats, &list_stats, _list_median, &list_median; - CImg mem_img_stats; - - CImg level, variable_pos, reserved_label; - CImgList variable_def, function_def, function_body; - char *user_function; - - unsigned int mempos, mem_img_median, debug_indent, init_size, result_dim; - bool is_parallelizable, need_input_copy; - double *result; - const char *const calling_function; - typedef double (*mp_func)(_cimg_math_parser&); - -#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant? -#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar? -#define _cimg_mp_is_temp(arg) (!memtype[arg]) // Is temporary scalar? -#define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable? -#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? -#define _cimg_mp_vector_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Vector size -#define _cimg_mp_calling_function calling_function_s()._data -#define _cimg_mp_check_type(arg,n_arg,s_op,mode,N) check_type(arg,n_arg,s_op,mode,N,ss,se,saved_char) -#define _cimg_mp_check_constant(arg,n_arg,s_op,is_strict) check_constant(arg,n_arg,s_op,is_strict,ss,se,saved_char) -#define _cimg_mp_check_matrix_square(arg,n_arg,s_op) check_matrix_square(arg,n_arg,s_op,ss,se,saved_char) -#define _cimg_mp_check_vector0(dim,s_op) check_vector0(dim,s_op,ss,se,saved_char) -#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) -#define _cimg_mp_return(x) { *se = saved_char; return x; } -#define _cimg_mp_constant(val) _cimg_mp_return(constant(val)) -#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) -#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) -#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) -#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) -#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) -#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) -#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) -#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) -#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) -#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) -#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) - - // Constructors. - _cimg_math_parser(const char *const expression, const char *const funcname=0, - const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, - const CImgList *const list_input=0, CImgList *const list_output=0): - code(_code),imgin(img_input),listin(list_input?*list_input:CImgList::const_empty()), - imgout(img_output?*img_output:CImg::empty()),listout(list_output?*list_output:CImgList::empty()), - img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_function(0), - mem_img_median(~0U),debug_indent(0),init_size(0),result_dim(0),is_parallelizable(true), - need_input_copy(false),calling_function(funcname?funcname:"cimg_math_parser") { - if (!expression || !*expression) - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Empty expression.", - pixel_type(),_cimg_mp_calling_function); - const char *_expression = expression; - while (*_expression && *_expression<=' ') ++_expression; - CImg::string(_expression).move_to(expr); - - // Ease the retrieval of previous non-space characters afterwards. - pexpr.assign(expr._width); - const char *ps; - char c, *pe = pexpr._data; - for (ps = expr._data, c = ' '; *ps; ++ps) { - if (*ps!=' ') c = *ps; - *(pe++) = c; - } - *pe = 0; - - // Count parentheses/brackets level of expression. - level.assign(expr._width - 1); - int lv = 0; - unsigned int *pd = level._data; - for (ps = expr._data; *ps && lv>=0; ++ps) - *(pd++) = (unsigned int)(*ps=='('||*ps=='['?lv++:*ps==')'||*ps==']'?--lv:lv); - if (lv!=0) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", - pixel_type(),_cimg_mp_calling_function, - expr._data); - } - - // Init constant values. - mem.assign(96); - memtype.assign(96); - double *p_mem = mem._data; - for (unsigned int i = 0; i<=10; ++i) *(p_mem++) = (double)i; // mem[0-10] - for (unsigned int i = 1; i<=5; ++i) *(p_mem++) = -(double)i; // mem[11-15] - *(p_mem++) = 0.5; // mem[16] - *(p_mem++) = 0; // mem[17] = thread_id - *(p_mem++) = (double)imgin._width; // mem[18] - *(p_mem++) = (double)imgin._height; // mem[19] - *(p_mem++) = (double)imgin._depth; // mem[20] - *(p_mem++) = (double)imgin._spectrum; // mem[21] - *(p_mem++) = (double)imgin._is_shared; // mem[22] - *(p_mem++) = (double)imgin._width*imgin._height; // mem[23] - *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth; // mem[24] - *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // mem[25] - *(p_mem++) = cimg::PI; // mem[26] - *(p_mem++) = std::exp(1.0); // mem[27] - *(p_mem++) = cimg::type::nan(); // mem[28] - - // Then, [29] = x, [30] = y, [31] = z and [32] = c. -#define _cimg_mp_x 29 -#define _cimg_mp_y 30 -#define _cimg_mp_z 31 -#define _cimg_mp_c 32 - - // Set value property : - // { -1 = variable | 0 = regular value | 1 = compile time constant | N>1 = constant ptr to vector[N-1] }. - std::memset(memtype._data,0,sizeof(int)*memtype._width); - int *p_memtype = memtype._data; for (unsigned int i = 0; i<_cimg_mp_x; ++i) *(p_memtype++) = 1; - memtype[17] = 0; - - mempos = _cimg_mp_c + 1; - variable_pos.assign(8); - reserved_label.assign(128,1,1,1,~0U); - reserved_label['t'] = 17; - reserved_label['w'] = 18; - reserved_label['h'] = 19; - reserved_label['d'] = 20; - reserved_label['s'] = 21; - reserved_label['r'] = 22; - reserved_label[0] = 23; // wh - reserved_label[1] = 24; // whd - reserved_label[2] = 25; // whds - reserved_label[3] = 26; // pi - reserved_label['e'] = 27; - reserved_label[29] = 0; // interpolation - reserved_label[30] = 0; // boundary - reserved_label['x'] = _cimg_mp_x; - reserved_label['y'] = _cimg_mp_y; - reserved_label['z'] = _cimg_mp_z; - reserved_label['c'] = _cimg_mp_c; - // reserved_label[4-28] store also two-char variables: - // [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = is, [9] = ip, [10] = ic, - // [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM, [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, - - // Compile expression into a serie of opcodes. - const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0); - p_code_end = code.end(); - - // Free resources used for parsing and prepare for evaluation. - if (_cimg_mp_is_vector(ind_result)) result_dim = _cimg_mp_vector_size(ind_result); - mem.resize(mempos,1,1,1,-1); - result = mem._data + ind_result; - memtype.assign(); - level.assign(); - variable_pos.assign(); - reserved_label.assign(); - expr.assign(); - pexpr.assign(); - opcode.assign(); - opcode._width = opcode._depth = opcode._spectrum = 1; - opcode._is_shared = true; - - // Execute init() function if any specified. - p_code_begin = code._data + init_size; - if (init_size) { - mem[_cimg_mp_x] = mem[_cimg_mp_y] = mem[_cimg_mp_z] = mem[_cimg_mp_c] = 0; - for (p_code = code._data; p_code &op = *p_code; - opcode._data = op._data; opcode._height = op._height; - const uptrT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - } - } - - _cimg_math_parser(): - code(_code),p_code_begin(0),p_code_end(0), - imgin(CImg::const_empty()),listin(CImgList::const_empty()), - imgout(CImg::empty()),listout(CImgList::empty()), - img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0), - result_dim(0),is_parallelizable(true),need_input_copy(false),calling_function(0) { - mem.assign(1 + _cimg_mp_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() - result = mem._data; - } - - _cimg_math_parser(const _cimg_math_parser& mp): - mem(mp.mem),code(mp.code),p_code_begin(mp.p_code_begin),p_code_end(mp.p_code_end), - imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats), - list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),result_dim(mp.result_dim), - is_parallelizable(mp.is_parallelizable), need_input_copy(mp.need_input_copy), - result(mem._data + (mp.result - mp.mem._data)),calling_function(0) { -#ifdef cimg_use_openmp - mem[17] = omp_get_thread_num(); -#endif - opcode._width = opcode._depth = opcode._spectrum = 1; - opcode._is_shared = true; - } - - // Compilation procedure. - unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref) { - if (depth>256) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Call stack overflow (infinite recursion?), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - const char *const ss0 = ss; - char c1, c2, c3, c4; - - if (ssss && (c1=*(se - 1))>0 && (c1<=' ' || c1==';')) --se; - } - if (se>ss && *(se - 1)==';') --se; - while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Detect simple content around parentheses. - ++ss; --se; - } - if (se<=ss || !*ss) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Missing item, in expression '%s'.", - pixel_type(),_cimg_mp_calling_function, - expr._data); - } - const unsigned int depth1 = depth + 1; - unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; - char - *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, - *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 ss8 = ss + 8, - *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; - double val, val1, val2; - const char *s_op; - mp_func op; - - // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value - // linked to the returned memory slot (reference that cannot be determined at compile time). - // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | - // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | - // 5 = image value as a vector (coordinates) }. - // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: - // When p_ref[0]==0, p_ref is actually unlinked. - // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. - // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. - // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. - // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. - // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. - if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } - - const char saved_char = *se; *se = 0; - const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; - bool is_sth, is_relative; - CImg ref; - CImgList _opcode; - CImg variable_name; - - // Look for a single value or a pre-defined variable. - int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); - -#if cimg_OS==2 - // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able - // to read those particular values. - if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) { - is_sth = true; - s = ss; - if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; } - if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } - else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } - if (nb==1 && !is_sth) val = -val; - } -#endif - if (nb==1) _cimg_mp_constant(val); - if (nb==2 && sep=='%') _cimg_mp_constant(val/100); - - if (ss1==se) switch (*ss) { // One-char variable - case 't' : case 'w' : case 'h' : case 'd' : case 's' : case 'r' : - case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : - _cimg_mp_return(reserved_label[*ss]); - case 'u' : - if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']); - _cimg_mp_scalar2(mp_u,0,1); - case 'g' : - if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']); - _cimg_mp_scalar0(mp_g); - case 'i' : - if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']); - _cimg_mp_scalar0(mp_i); - case 'I' : - if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']); - _cimg_mp_check_vector0(imgin._spectrum,"variable 'I'"); - need_input_copy = true; - pos = vector(imgin._spectrum); - CImg::vector((uptrT)mp_Joff,pos,0,0).move_to(code); - _cimg_mp_return(pos); - case 'R' : - if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0,0,0); - case 'G' : - if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1,0,0); - case 'B' : - if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2,0,0); - case 'A' : - if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3,0,0); - } - else if (ss2==se) { // Two-chars variable - arg1 = arg2 = ~0U; - if (*ss=='w' && *ss1=='h') _cimg_mp_return(reserved_label[0]); // wh - if (*ss=='p' && *ss1=='i') _cimg_mp_return(reserved_label[3]); // pi - if (*ss=='i') { - if (*ss1>='0' && *ss1<='9') { // i0...i9 - pos = 19 + *ss1 - '0'; - if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,pos - 19,0,0); - } - switch (*ss1) { - case 'm' : arg1 = 4; arg2 = 0; break; // im - case 'M' : arg1 = 5; arg2 = 1; break; // iM - case 'a' : arg1 = 6; arg2 = 2; break; // ia - case 'v' : arg1 = 7; arg2 = 3; break; // iv - case 's' : arg1 = 8; arg2 = 12; break; // is - case 'p' : arg1 = 9; arg2 = 13; break; // is - case 'c' : // ic - if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); - if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; - _cimg_mp_return(mem_img_median); - break; - } - } - else if (*ss1=='m') switch (*ss) { - case 'x' : arg1 = 11; arg2 = 4; break; // xm - case 'y' : arg1 = 12; arg2 = 5; break; // ym - case 'z' : arg1 = 13; arg2 = 6; break; // zm - case 'c' : arg1 = 14; arg2 = 7; break; // cm - } - else if (*ss1=='M') switch (*ss) { - case 'x' : arg1 = 15; arg2 = 8; break; // xM - case 'y' : arg1 = 16; arg2 = 9; break; // yM - case 'z' : arg1 = 17; arg2 = 10; break; // zM - case 'c' : arg1 = 18; arg2 = 11; break; // cM - } - if (arg1!=~0U) { - if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); - if (!img_stats) { - img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); - mem_img_stats.assign(1,14,1,1,~0U); - } - if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); - _cimg_mp_return(mem_img_stats[arg2]); - } - } else if (ss3==se) { // Three-chars variable - if (*ss=='w' && *ss1=='h' && *ss2=='d') _cimg_mp_return(reserved_label[1]); // whd - } else if (ss4==se) { // Four-chars variable - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') _cimg_mp_return(reserved_label[2]); // whds - } - - pos = ~0U; - for (s0 = ss, s = ss1; s2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') && - (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) { - is_relative = *ss=='j' || *ss=='J'; - - if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value - is_parallelizable = false; - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0='i'?1:3,p2); - - if (p_ref) { - *p_ref = _cimg_mp_is_vector(arg2)?4:2; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - if (_cimg_mp_is_vector(arg2)) - set_variable_vector(arg2); // Prevent from being used in further optimization - else if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; - if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; - if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; - } - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (*ss>='i') - CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg2,p1,arg1).move_to(code); - else if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), - arg2,p1,arg1).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg1).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (*ss>='i') - CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), - arg2,arg1).move_to(code); - if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), - arg2,arg1).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg1).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value - is_parallelizable = false; - if (*ss2=='#') { // Index specified - s0 = ss3; while (s01) { - arg2 = arg1 + 1; - if (p2>2) { - arg3 = arg2 + 1; - if (p2>3) arg4 = arg3 + 1; - } - } - } else if (s1='i'?1:3,p2); - - if (p_ref) { - *p_ref = _cimg_mp_is_vector(arg5)?5:3; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - p_ref[4] = arg2; - p_ref[5] = arg3; - p_ref[6] = arg4; - if (_cimg_mp_is_vector(arg5)) - set_variable_vector(arg5); // Prevent from being used in further optimization - else if (_cimg_mp_is_temp(arg5)) memtype[arg5] = -1; - if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; - if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; - if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; - if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; - if (_cimg_mp_is_temp(arg4)) memtype[arg4] = -1; - } - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg5); - if (*ss>='i') - CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg5,p1,arg1,arg2,arg3,arg4).move_to(code); - else if (_cimg_mp_is_scalar(arg5)) - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), - arg5,p1,arg1,arg2,arg3).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg5,p1,arg1,arg2,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg5); - if (*ss>='i') - CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg5,arg1,arg2,arg3,arg4).move_to(code); - else if (_cimg_mp_is_scalar(arg5)) - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), - arg5,arg1,arg2,arg3).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg5,arg1,arg2,arg3).move_to(code); - } - _cimg_mp_return(arg5); - } - } - - // Assign vector value (direct). - if (l_variable_name>3 && *ve1==']' && *ss!='[') { - s0 = ve1; while (s0>ss && *s0!='[') --s0; - is_sth = true; // is_valid_variable_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; nsss) { - variable_name[s0 - ss] = 0; // Remove brackets in variable name - arg1 = ~0U; // Vector slot - arg2 = compile(++s0,ve1,depth1,0); // Index - arg3 = compile(s + 1,se,depth1,0); // Value to assign - _cimg_mp_check_type(arg3,2,s_op,1,0); - - if (variable_name[1]) { // Multi-char variable - cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) { - arg1 = variable_pos[i]; break; - } - } else arg1 = reserved_label[*variable_name]; // Single-char variable - if (arg1==~0U) compile(ss,s0 - 1,depth1,0); // Variable does not exist -> error - else { // Variable already exists - if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0); // Variable is not a vector -> error - if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly - nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) { - arg1+=nb + 1; - CImg::vector((uptrT)mp_copy,arg1,arg3).move_to(code); - _cimg_mp_return(arg1); - } - compile(ss,s,depth1,0); // Out-of-bounds reference -> error - } - - // Case of non-constant index -> return assigned value + linked reference - if (p_ref) { - *p_ref = 1; - p_ref[1] = arg1; - p_ref[2] = arg2; - if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization - if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; - } - CImg::vector((uptrT)mp_vector_set_off,arg3,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2,arg3). - move_to(code); - _cimg_mp_return(arg3); - } - } - } - - // Assign user-defined macro. - if (l_variable_name>3 && *ve1==')' && *ss!='(') { - s0 = ve1; while (s0>ss && *s0!='(') --s0; - is_sth = std::strncmp(variable_name,"debug(",6) && - std::strncmp(variable_name,"print(",6); // is_valid_function_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; nsss) { // Looks like a valid function declaration - s0 = variable_name._data + (s0 - ss); - *s0 = 0; - s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis - CImg(variable_name._data,s0 - variable_name._data + 1).move_to(function_def,0); - ++s; while (*s && *s<=' ') ++s; - CImg(s,se - s + 1).move_to(function_body,0); - - p1 = 1; // Indice of current parsed argument - for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments - if (p1>24) { - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Too much specified arguments (>24) when defining " - "function '%s()', in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - while (*s && *s<=' ') ++s; - if (*s==')' && p1==1) break; // Function has no arguments - - s2 = s; // Start of the argument name - is_sth = true; // is_valid_argument_name? - if (*s>='0' && *s<='9') is_sth = false; - else for (ns = s; ns' '; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - s3 = ns; // End of the argument name - while (*ns && *ns<=' ') ++ns; - if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) { - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: %s name specified for argument %u when defining " - "function '%s()', in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - is_sth?"Empty":"Invalid",p1, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - if (ns==s1 || *ns==',') { // New argument found - *s3 = 0; - p2 = s3 - s2; // Argument length - p3 = function_body[0]._width - p2 + 1; // Related to copy length - for (ps = std::strstr(function_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number - if (!((ps>function_body[0]._data && is_varchar(*(ps - 1))) || - (ps + p21) { - std::memmove(ps,ps + p2 - 1,function_body[0]._data + p3 - ps); - function_body[0]._width-=p2 - 1; - } - } else ++ps; - } - } - } - // Store number of arguments - function_def[0].resize(function_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1); - _cimg_mp_return(28); - } - } - - // Check if the variable name could be valid. If not, this is probably an lvalue assignment. - is_sth = true; // is_valid_variable_name? - if (*variable_name>='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name._data; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - // Assign variable (direct). - if (is_sth) { - if (variable_name[1] && !variable_name[2]) { // Two-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - if (c1=='w' && c2=='h') variable_name.fill((char)0,(char)0); // wh - else if (c1=='p' && c2=='i') variable_name.fill(3,0); // pi - else if (c1=='i') { - if (c2>='0' && c2<='9') variable_name.fill(19 + c2 - '0',0); // i0...i9 - else if (c2=='m') variable_name.fill(4,0); // im - else if (c2=='M') variable_name.fill(5,0); // iM - else if (c2=='a') variable_name.fill(6,0); // ia - else if (c2=='v') variable_name.fill(7,0); // iv - else if (c2=='s') variable_name.fill(8,0); // is - else if (c2=='p') variable_name.fill(9,0); // ip - else if (c2=='c') variable_name.fill(10,0); // ic - } else if (c2=='m') { - if (c1=='x') variable_name.fill(11,0); // xm - else if (c1=='y') variable_name.fill(12,0); // ym - else if (c1=='z') variable_name.fill(13,0); // zm - else if (c1=='c') variable_name.fill(14,0); // cm - } else if (c2=='M') { - if (c1=='x') variable_name.fill(15,0); // xM - else if (c1=='y') variable_name.fill(16,0); // yM - else if (c1=='z') variable_name.fill(17,0); // zM - else if (c1=='c') variable_name.fill(18,0); // cM - } - } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - c3 = variable_name[2]; - if (c1=='w' && c2=='h' && c3=='d') variable_name.fill(1,0); // whd - } else if (variable_name[1] && variable_name[2] && variable_name[3] && - !variable_name[4]) { // Four-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - c3 = variable_name[2]; - c4 = variable_name[3]; - if (c1=='w' && c2=='h' && c3=='d' && c4=='s') variable_name.fill(2,0); // whds - } else if (!std::strcmp(variable_name,"interpolation")) variable_name.fill(29,0); - else if (!std::strcmp(variable_name,"boundary")) variable_name.fill(30,0); - - arg1 = ~0U; - arg2 = compile(s + 1,se,depth1,0); - if (!variable_name[1]) // One-char variable, or variable in reserved_labels - arg1 = reserved_label[*variable_name]; - else // Multi-char variable name : check for existing variable with same name - cimglist_for(variable_def,i) - if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; } - - if (arg1==~0U || arg1<=_cimg_mp_c) { // Create new variable - if (_cimg_mp_is_vector(arg2)) { // Vector variable - arg1 = vector_copy(arg2); - set_variable_vector(arg1); - } else { // Scalar variable - arg1 = scalar1(mp_copy,arg2); - memtype[arg1] = -1; - } - - if (!variable_name[1]) reserved_label[*variable_name] = arg1; - else { - if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); - variable_pos[variable_def._width] = arg1; - variable_name.move_to(variable_def); - } - - } else { // Variable already exists -> assign a new value - _cimg_mp_check_type(arg2,2,s_op,_cimg_mp_is_vector(arg1)?3:1,0); - if (_cimg_mp_is_vector(arg1)) { // Vector - if (_cimg_mp_is_vector(arg2)) // From vector - CImg::vector((uptrT)mp_vector_copy,arg1,arg2,(uptrT)_cimg_mp_vector_size(arg1)). - move_to(code); - else // From scalar - CImg::vector((uptrT)mp_vector_init,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2). - move_to(code); - } else // Scalar - CImg::vector((uptrT)mp_copy,arg1,arg2).move_to(code); - } - _cimg_mp_return(arg1); - } - - // Assign lvalue (variable name was not valid). - is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? - if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment - - if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { - ref.assign(7); - arg1 = compile(ss,s,depth1,ref); // Lvalue slot - arg2 = compile(s + 1,se,depth1,0); // Value to assign - - if (*ref==1) { // Vector value (scalar): V[k] = scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)mp_vector_set_off,arg2,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg2). - move_to(code); - _cimg_mp_return(arg2); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg2,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), - arg2,arg3).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg2,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg2,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value - _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), - arg2,p1,arg3).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), - arg2,arg3).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg3).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value - _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), - arg2,p1,arg3,arg4,arg5).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg2,p1,arg3,arg4,arg5).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), - arg2,arg3,arg4,arg5).move_to(code); - else - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg2,arg3,arg4,arg5).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value - _cimg_mp_check_type(arg2,2,s_op,1,0); - if (_cimg_mp_is_vector(arg2)) // From vector - CImg::vector((uptrT)mp_vector_copy,arg1,arg2,(uptrT)_cimg_mp_vector_size(arg1)). - move_to(code); - else // From scalar - CImg::vector((uptrT)mp_vector_init,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2). - move_to(code); - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - CImg::vector((uptrT)mp_copy,arg1,arg2).move_to(code); - _cimg_mp_return(arg1); - - } - } - - // No assignment expressions match -> error - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Invalid left-hand operand '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - // Apply unary/binary/ternary operators. The operator precedences should be roughly the same as in C++. - for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 - if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && - level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) - s_op = *ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"; - - ref.assign(7); - arg1 = compile(ss,ns,depth1,ref); // Vector slot - arg2 = compile(s + 1,se,depth1,0); // Right operand - if (*ps!='*') { - _cimg_mp_check_type(arg1,2,s_op,2,2); - _cimg_mp_check_type(arg2,2,s_op,2,2); - } - if (_cimg_mp_is_vector(arg2)) { // Complex **= complex or Matrix **= matrix - if (*ps=='*') { - if (_cimg_mp_vector_size(arg1)==2 && _cimg_mp_vector_size(arg2)==2) - CImg::vector((uptrT)mp_complex_mul,arg1,arg1,arg2).move_to(code); - else { - _cimg_mp_check_matrix_square(arg2,2,s_op); - p3 = _cimg_mp_vector_size(arg1); - p2 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg2)); - p1 = p3/p2; - if (p1*p2!=p3) { - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Sizes of left-hand and right-hand operands " - "('%s' and '%s') do not match, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - CImg::vector((uptrT)mp_matrix_mul,arg1,arg1,arg2,p1,p2,p2).move_to(code); - } - } else if (*ps=='/') - CImg::vector((uptrT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); - else - CImg::vector((uptrT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); - } else { // Complex **= scalar - if (*ps=='*') self_vector_s(arg1,mp_self_mul,arg2); - else if (*ps=='/') self_vector_s(arg1,mp_self_div,arg2); - else CImg::vector((uptrT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); - } - - // Write computed value back in image if necessary. - if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3).move_to(code); - } - - } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5).move_to(code); - } - } - - _cimg_mp_return(arg1); - } - - for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 - if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || - *ps=='&' || *ps=='^' || *ps=='|' || - (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && - level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) - switch (*ps) { - case '+' : op = mp_self_add; s_op = "Operator '+='"; break; - case '-' : op = mp_self_sub; s_op = "Operator '-='"; break; - case '*' : op = mp_self_mul; s_op = "Operator '*='"; break; - case '/' : op = mp_self_div; s_op = "Operator '/='"; break; - case '%' : op = mp_self_modulo; s_op = "Operator '%='"; break; - case '<' : op = mp_self_bitwise_left_shift; s_op = "Operator '<<='"; break; - case '>' : op = mp_self_bitwise_right_shift; s_op = "Operator '>=='"; break; - case '&' : op = mp_self_bitwise_and; s_op = "Operator '&='"; break; - case '|' : op = mp_self_bitwise_or; s_op = "Operator '|='"; break; - default : op = mp_self_pow; s_op = "Operator '^='"; break; - } - s1 = *ps=='>' || *ps=='<'?ns:ps; - - ref.assign(7); - arg1 = compile(ss,s1,depth1,ref); // Variable slot - arg2 = compile(s + 1,se,depth1,0); // Value to apply - - if (*ref>0 && !_cimg_mp_is_temp(arg1)) { // Apply operator on a copy if necessary. - if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); - else arg1 = scalar1(mp_copy,arg1); - } - - if (*ref==1) { // Vector value (scalar): V[k] += scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1,arg2).move_to(code); - CImg::vector((uptrT)mp_vector_set_off,arg1,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg1). - move_to(code); - _cimg_mp_return(arg1); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1,arg2).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), - arg1,arg3).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1,arg2).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg1,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg1,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value - _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value - _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value - _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector - else self_vector_s(arg1,op,arg2); // Vector += scalar - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar - _cimg_mp_check_type(arg2,2,s_op,1,0); - CImg::vector((uptrT)op,arg1,arg2).move_to(code); - _cimg_mp_return(arg1); - } - - variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Invalid left-hand operand '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - for (s = ss1; s::vector((uptrT)mp_if,pos,arg1,arg2,arg3, - p3 - p2,code._width - p3,arg4).move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') - s_op = "Operator '||'"; - arg1 = compile(ss,s,depth1,0); - p2 = code._width; - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg1,1,s_op,1,0); - _cimg_mp_check_type(arg2,2,s_op,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] || mem[arg2]); - pos = scalar(); - CImg::vector((uptrT)mp_logical_or,pos,arg1,arg2,code._width - p2). - move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') - s_op = "Operator '&&'"; - arg1 = compile(ss,s,depth1,0); - p2 = code._width; - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg1,1,s_op,1,0); - _cimg_mp_check_type(arg2,2,s_op,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] && mem[arg2]); - pos = scalar(); - CImg::vector((uptrT)mp_logical_and,pos,arg1,arg2,code._width - p2). - move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se2; s>ss; --s) - if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '|'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((unsigned long)mem[arg1] | (unsigned long)mem[arg2]); - _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); - } - - for (s = se2; s>ss; --s) - if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '&'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((unsigned long)mem[arg1] & (unsigned long)mem[arg2]); - _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '!='",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_neq,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_neq,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_neq,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); - _cimg_mp_scalar2(mp_neq,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '=='",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_eq,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_eq,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_eq,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); - _cimg_mp_scalar2(mp_eq,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '<='",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); - _cimg_mp_scalar2(mp_lte,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '>='",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); - _cimg_mp_scalar2(mp_gte,arg1,arg2); - } - - for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) - if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '<'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) - if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '>'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); - _cimg_mp_scalar2(mp_gt,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '<<'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) - _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((long)mem[arg1]<<(unsigned int)mem[arg2]); - _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '>>'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) - _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((long)mem[arg1]>>(unsigned int)mem[arg2]); - _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); - } - - for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) - if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && - (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && - *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) { // Addition ('+') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '+'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); - if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); - if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); - _cimg_mp_scalar2(mp_add,arg1,arg2); - } - - for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) - if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && - (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && - *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) { // Subtraction ('-') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '-'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_sub,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); - if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); - _cimg_mp_scalar2(mp_sub,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex/matrix multiplication ('**') - s_op = "Operator '**'"; - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - if (_cimg_mp_vector_size(arg1)==2 && _cimg_mp_vector_size(arg2)==2) { // Complex multiplication - pos = vector(2); - CImg::vector((uptrT)mp_complex_mul,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } else { // Matrix multiplication - p1 = _cimg_mp_vector_size(arg1); - p2 = _cimg_mp_vector_size(arg2); - arg4 = p1/p2; - if (arg4*p2!=p1) { - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Sizes of left-hand and right-hand operands " - "('%s' and '%s') do not match, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - pos = vector(arg4); - CImg::vector((uptrT)mp_matrix_mul,pos,arg1,arg2,arg4,p2,1).move_to(code); - _cimg_mp_return(pos); - } - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); - _cimg_mp_scalar2(mp_mul,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') - s_op = "Operator '//'"; - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg1,1,s_op,3,2); - _cimg_mp_check_type(arg2,2,s_op,3,2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - pos = vector(2); - CImg::vector((uptrT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - pos = vector(2); - CImg::vector((uptrT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); - _cimg_mp_scalar2(mp_div,arg1,arg2); - } - - for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '*'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); - _cimg_mp_scalar2(mp_mul,arg1,arg2); - } - - - for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '/'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); - _cimg_mp_scalar2(mp_div,arg1,arg2); - } - - for (s = se2, ns = se1; s>ss; --s, --ns) - if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '%'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); - _cimg_mp_scalar2(mp_modulo,arg1,arg2); - } - - if (se1>ss) { - if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) // Unary plus ('+') - _cimg_mp_return(compile(ss1,se,depth1,0)); - - if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') - arg1 = compile(ss1,se,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); - _cimg_mp_scalar1(mp_minus,arg1); - } - - if (*ss=='!') { // Logical not ('!') - arg1 = compile(ss1,se,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); - _cimg_mp_scalar1(mp_logical_not,arg1); - } - - if (*ss=='~') { // Bitwise not ('~') - arg1 = compile(ss1,se,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned long)mem[arg1]); - _cimg_mp_scalar1(mp_bitwise_not,arg1); - } - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') - s_op = "Operator '^^'"; - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg1,1,s_op,3,2); - _cimg_mp_check_type(arg2,2,s_op,3,2); - pos = (_cimg_mp_is_vector(arg1) || _cimg_mp_is_vector(arg2))?vector(2):0; - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - CImg::vector((uptrT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { - CImg::vector((uptrT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - CImg::vector((uptrT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); - switch (arg2) { - case 0 : _cimg_mp_return(1); - case 1 : _cimg_mp_return(arg1); - case 2 : _cimg_mp_scalar1(mp_sqr,arg1); - case 3 : _cimg_mp_scalar1(mp_pow3,arg1); - case 4 : _cimg_mp_scalar1(mp_pow4,arg1); - default : _cimg_mp_scalar2(mp_pow,arg1,arg2); - } - } - - for (s = se2; s>ss; --s) - if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,"operator '^'",3,_cimg_mp_vector_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); - switch (arg2) { - case 0 : _cimg_mp_return(1); - case 1 : _cimg_mp_return(arg1); - case 2 : _cimg_mp_scalar1(mp_sqr,arg1); - case 3 : _cimg_mp_scalar1(mp_pow3,arg1); - case 4 : _cimg_mp_scalar1(mp_pow4,arg1); - default : _cimg_mp_scalar2(mp_pow,arg1,arg2); - } - } - - is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment - if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { op = mp_self_increment; s_op = "Operator '++'"; } - else { op = mp_self_decrement; s_op = "Operator '--'"; } - - ref.assign(7); - arg1 = is_sth?compile(ss2,se,depth1,ref):compile(ss,se2,depth1,ref); // Variable slot - - if (*ref>0 && !_cimg_mp_is_temp(arg1)) { // Apply operator on a copy if necessary. - if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); - else arg1 = scalar1(mp_copy,arg1); - } - - if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action - else { - if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); - else pos = scalar1(mp_copy,arg1); - } - - if (*ref==1) { // Vector value (scalar): V[k]++ - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1,1).move_to(code); - CImg::vector((uptrT)mp_vector_set_off,arg1,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg1). - move_to(code); - _cimg_mp_return(pos); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), - arg1,arg3).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((uptrT)op,arg1).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg1,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg1,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ - is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5).move_to(code); - } - _cimg_mp_return(pos); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - _cimg_mp_return(pos); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++ - CImg::vector((uptrT)op,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); - else variable_name.assign(ss,(unsigned int)(se1 - ss)); - variable_name.back() = 0; - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Invalid operand '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - // Array-like access to vectors and image values 'i/j[_#ind,offset,_boundary]' and 'vector[offset]'. - if (*se1==']' && *ss!='[') { - s_op = "Operator '[]'"; - is_relative = *ss=='j' || *ss=='J'; - - if ((*ss=='I' || *ss=='J') && *ss1=='[' && - (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0::vector((uptrT)(is_relative?mp_list_Joff:mp_list_Ioff), - pos,p1,arg1,arg2==~0U?reserved_label[30]:arg2).move_to(code); - } else { - need_input_copy = true; - CImg::vector((uptrT)(is_relative?mp_Joff:mp_Ioff), - pos,arg1,arg2==~0U?reserved_label[30]:arg2).move_to(code); - } - _cimg_mp_return(pos); - } - - if ((*ss=='i' || *ss=='j') && *ss1=='[' && - (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0ss && *s0!='[') --s0; - if (s0>ss) { // Vector value - arg1 = compile(ss,s0,depth1,0); - s1 = s0 + 1; while (s1 sub-vector extraction - arg2 = compile(++s0,s1,depth1,0); - arg3 = compile(++s1,se1,depth1,0); - _cimg_mp_check_constant(arg2,1,s_op,false); - _cimg_mp_check_constant(arg3,2,s_op,false); - p1 = (unsigned int)mem[arg2]; - p2 = (unsigned int)mem[arg3]; - p3 = _cimg_mp_vector_size(arg1); - if (p1>=p3 || p2>=p3) { - variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Out-of-bounds request for sub-vector '%s[%d,%d]' " - "(vector '%s' has dimension %u), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data,(int)mem[arg2],(int)mem[arg3], - variable_name._data,p3, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - if (p1>p2) cimg::swap(p1,p2); - (p2-=p1)++; - pos = vector(p2); - CImg::vector((uptrT)mp_vector_crop,pos,arg1,p1,p2).move_to(code); - _cimg_mp_return(pos); - } - - // One argument -> vector value reference - if (_cimg_mp_is_scalar(arg1)) { - variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - arg2 = compile(++s0,se1,depth1,0); - if (_cimg_mp_is_constant(arg2)) { // Constant index - nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); - variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " - "(vector '%s' has dimension %u), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - variable_name._data,nb, - variable_name._data,_cimg_mp_vector_size(arg1), - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - if (p_ref) { - *p_ref = 1; - p_ref[1] = arg1; - p_ref[2] = arg2; - if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization - } - _cimg_mp_scalar3(mp_vector_off,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2); - } - } - - // Look for a function call, an access to image value, or a parenthesis. - if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref)); // Simple parentheses - is_relative = *ss=='j' || *ss=='J'; - - // I/J(_#ind,_x,_y,_z,_c,_interpolation,_boundary) - if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s01) { - arg2 = arg1 + 1; - if (p2>2) arg3 = arg2 + 1; - } - if (s1::vector((uptrT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), - pos,p1,arg1,arg2,arg3, - arg4==~0U?reserved_label[29]:arg4, - arg5==~0U?reserved_label[30]:arg5).move_to(code); - else { - need_input_copy = true; - CImg::vector((uptrT)(is_relative?mp_Jxyz:mp_Ixyz), - pos,arg1,arg2,arg3, - arg4==~0U?reserved_label[29]:arg4, - arg5==~0U?reserved_label[30]:arg5).move_to(code); - } - _cimg_mp_return(pos); - } - - // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary) - if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s01) { - arg2 = arg1 + 1; - if (p2>2) { - arg3 = arg2 + 1; - if (p2>3) arg4 = arg3 + 1; - } - } - if (s1::vector((uptrT)mp_complex_conj,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential - arg1 = compile(ss5,se1,depth1,0); - _cimg_mp_check_type(arg1,0,"Function 'cexp()'",2,2); - pos = vector(2); - CImg::vector((uptrT)mp_complex_exp,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm - arg1 = compile(ss5,se1,depth1,0); - _cimg_mp_check_type(arg1,0,"Function 'clog()'",2,2); - pos = vector(2); - CImg::vector((uptrT)mp_complex_log,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"copy(",5)) { // Memory copy - s_op = "Function 'copy()'"; - ref.assign(14); - s1 = ss5; while (s1(1,21).move_to(code); - code.back().get_shared_rows(0,6).fill((uptrT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5); - code.back().get_shared_rows(7,20).fill(ref); - _cimg_mp_return(p1); - } - - if (!std::strncmp(ss,"cos(",4)) { // Cosine - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); - _cimg_mp_scalar1(mp_cos,arg1); - } - - if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine - arg1 = compile(ss5,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); - _cimg_mp_scalar1(mp_cosh,arg1); - } - - if (!std::strncmp(ss,"crop(",5)) { // Image crop - s_op = "Function 'crop()'"; - if (*ss5=='#') { // Index specified - s0 = ss6; while (s0::sequence((uptrT)_cimg_mp_vector_size(arg1),arg1 + 1, - arg1 + (uptrT)_cimg_mp_vector_size(arg1)); - opcode.resize(1,cimg::min(opcode._height,4U),1,1,0).move_to(_opcode); - is_sth = true; - } else { - _cimg_mp_check_type(arg1,pos + 1,s_op,1,0); - CImg::vector(arg1).move_to(_opcode); - } - s = ns; - } - (_opcode>'y').move_to(opcode); - arg1 = 0; arg2 = p1!=~0U?1:0; - switch (opcode._height) { - case 0 : case 1 : - CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); - break; - case 2 : - CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,reserved_label[30]).move_to(opcode); - arg1 = arg2?3:2; - break; - case 3 : - CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); - arg1 = arg2?3:2; - break; - case 4 : - CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,reserved_label[30]). - move_to(opcode); - arg1 = (is_sth?2:1) + arg2; - break; - case 5 : - CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). - move_to(opcode); - arg1 = (is_sth?2:1) + arg2; - break; - case 6 : - CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - reserved_label[30]).move_to(opcode); - arg1 = (is_sth?2:4) + arg2; - break; - case 7 : - CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - opcode[6]).move_to(opcode); - arg1 = (is_sth?2:4) + arg2; - break; - case 8 : - CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], - opcode[7],reserved_label[30]).move_to(opcode); - arg1 = (is_sth?2:5) + arg2; - break; - case 9 : - arg1 = (is_sth?2:5) + arg2; - break; - default : // Error -> too much arguments - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Function '%s': Too much arguments specified, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - _cimg_mp_check_type(*opcode,arg2 + 1,s_op,1,0); - _cimg_mp_check_type(opcode[1],arg2 + (is_sth?0:1),s_op,1,0); - _cimg_mp_check_type(opcode[2],arg2 + (is_sth?0:2),s_op,1,0); - _cimg_mp_check_type(opcode[3],arg2 + (is_sth?0:3),s_op,1,0); - if (opcode[4]!=(uptrT)~0U) { - _cimg_mp_check_constant(opcode[4],arg1,s_op,true); - opcode[4] = (uptrT)mem[opcode[4]]; - } - if (opcode[5]!=(uptrT)~0U) { - _cimg_mp_check_constant(opcode[5],arg1 + 1,s_op,true); - opcode[5] = (uptrT)mem[opcode[5]]; - } - if (opcode[6]!=(uptrT)~0U) { - _cimg_mp_check_constant(opcode[6],arg1 + 2,s_op,true); - opcode[6] = (uptrT)mem[opcode[6]]; - } - if (opcode[7]!=(uptrT)~0U) { - _cimg_mp_check_constant(opcode[7],arg1 + 3,s_op,true); - opcode[7] = (uptrT)mem[opcode[7]]; - } - _cimg_mp_check_type(opcode[8],arg1 + 4,s_op,1,0); - - if (opcode[4]==(uptrT)~0U || opcode[5]==(uptrT)~0U || - opcode[6]==(uptrT)~0U || opcode[7]==(uptrT)~0U) { - if (p1!=~0U) { - _cimg_mp_check_constant(p1,1,s_op,false); - p1 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - } - const CImg &img = p1!=~0U?listin[p1]:imgin; - if (p1==~0U) need_input_copy = true; - if (!img) - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Function '%s': Cannot crop empty image when " - "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - if (opcode[4]==(uptrT)~0U) opcode[4] = (uptrT)img._width; - if (opcode[5]==(uptrT)~0U) opcode[5] = (uptrT)img._height; - if (opcode[6]==(uptrT)~0U) opcode[6] = (uptrT)img._depth; - if (opcode[7]==(uptrT)~0U) opcode[7] = (uptrT)img._spectrum; - } - - pos = vector(opcode[4]*opcode[5]*opcode[6]*opcode[7]); - if (p1!=~0U) - CImg::vector((uptrT)mp_list_crop, - pos,p1, - *opcode,opcode[1],opcode[2],opcode[3], - opcode[4],opcode[5],opcode[6],opcode[7], - opcode[8]).move_to(code); - else - CImg::vector((uptrT)mp_crop, - pos, - *opcode,opcode[1],opcode[2],opcode[3], - opcode[4],opcode[5],opcode[6],opcode[7], - opcode[8]).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cross(",6)) { // Cross product - s_op = "Function 'cross()'"; - s1 = ss6; while (s1::vector((uptrT)mp_cross,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cut(",4)) { // Cut - s1 = ss4; while (s1val2?val2:val); - } - _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); - } - break; - - case 'd' : - if (!std::strncmp(ss,"date(",5)) { // Date and file date - s1 = ss5; while (s1::vector((uptrT)mp_debug,arg1,code._width - p1), - CImg::string(ss6).unroll('y'))>'y').move_to(code,p1); - *se1 = ')'; - _cimg_mp_return(arg1); - } - - if (!std::strncmp(ss,"det(",4)) { // Matrix determinant - arg1 = compile(ss4,se1,depth1,0); - _cimg_mp_check_matrix_square(arg1,1,"Function 'det()'"); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); - _cimg_mp_scalar2(mp_det,arg1,p1); - } - - if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix - arg1 = compile(ss5,se1,depth1,0); - _cimg_mp_check_type(arg1,1,"Function 'diag()'",2,0); - p1 = _cimg_mp_vector_size(arg1); - pos = vector(p1*p1); - CImg::vector((uptrT)mp_diag,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"dot(",4)) { // Dot product - s_op = "Function 'dot()'"; - s1 = ss4; while (s1::vector((uptrT)mp_dowhile,arg1,arg2,code._width - p1).move_to(code,p1); - _cimg_mp_return(arg1); - } - break; - - case 'e' : - if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector - arg1 = compile(ss4,se1,depth1,0); - _cimg_mp_check_matrix_square(arg1,1,"Function 'eig()'"); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); - pos = vector((p1 + 1)*p1); - CImg::vector((uptrT)mp_eig,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"exp(",4)) { // Exponential - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); - _cimg_mp_scalar1(mp_exp,arg1); - } - - if (!std::strncmp(ss,"eye(",4)) { // Identity matrix - arg1 = compile(ss4,se1,depth1,0); - _cimg_mp_check_constant(arg1,1,"Function 'eye()'",true); - p1 = (unsigned int)mem[arg1]; - pos = vector(p1*p1); - CImg::vector((uptrT)mp_eye,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'f' : - if (*ss1=='o' && *ss2=='r' && (*ss3=='(' || (*ss3 && *ss3<=' ' && *ss4=='('))) { // For loop - if (*ss3<=' ') cimg::swap(*ss3,*ss4); // Allow space before opening brace - s1 = ss4; while (s1::vector((uptrT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2).move_to(code,p1); - _cimg_mp_return(pos); - } - break; - - case 'g' : - if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function - s1 = ss6; while (s10) { val/=val1; _cimg_mp_constant(val1*std::sqrt(1+val*val)); } - _cimg_mp_constant(0); - } - _cimg_mp_scalar2(mp_hypot,arg1,arg2); - } - break; - - case 'i' : - if (*ss1=='f' && (*ss2=='(' || (*ss2 && *ss2<=' ' && *ss3=='('))) { // If..then[..else.] - s_op = "Function 'if()'"; - if (*ss2<=' ') cimg::swap(*ss2,*ss3); // Allow space before opening brace - s1 = ss3; while (s1::vector((uptrT)mp_if,pos,arg1,arg2,arg3, - p3 - p2,code._width - p3,arg4).move_to(code,p2); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"init(",5)) { // Init - if (ss0!=expr._data || code.width()) { // (only allowed as the first instruction) - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Function 'init()': Init invokation not done at the " - "beginning of expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - arg1 = compile(ss5,se1,depth1,p_ref); - init_size = code.width(); - _cimg_mp_return(arg1); - } - - if (!std::strncmp(ss,"int(",4)) { // Integer cast - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((long)mem[arg1]); - _cimg_mp_scalar1(mp_int,arg1); - } - - if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion - arg1 = compile(ss4,se1,depth1,0); - _cimg_mp_check_matrix_square(arg1,1,"Function 'inv()'"); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); - pos = vector(p1*p1); - CImg::vector((uptrT)mp_inv,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='s') { // Family of 'is_?()' functions - - if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? - if (ss7==se1) _cimg_mp_return(0); - arg1 = compile(ss7,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0); - _cimg_mp_scalar1(mp_isbool,arg1); - } - - if (!std::strncmp(ss,"isdir(",6)) { // Is directory? - *se1 = 0; - is_sth = cimg::is_directory(ss6); - *se1 = ')'; - _cimg_mp_return(is_sth?1U:0U); - } - - if (!std::strncmp(ss,"isfile(",7)) { // Is file? - *se1 = 0; - is_sth = cimg::is_file(ss7); - *se1 = ')'; - _cimg_mp_return(is_sth?1U:0U); - } - - if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? - pos = scalar(); - CImg::vector((uptrT)mp_isin,pos).move_to(_opcode); - for (s = ss5; s::sequence((uptrT)_cimg_mp_vector_size(arg1),arg1 + 1, - arg1 + (uptrT)_cimg_mp_vector_size(arg1)). - move_to(_opcode); - else CImg::vector(arg1).move_to(_opcode); - s = ns; - } - (_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); - _cimg_mp_scalar1(mp_isinf,arg1); - } - - if (!std::strncmp(ss,"isint(",6)) { // Is integer? - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::mod(mem[arg1],1.0)==0); - _cimg_mp_scalar1(mp_isint,arg1); - } - - if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); - _cimg_mp_scalar1(mp_isnan,arg1); - } - - if (!std::strncmp(ss,"isval(",6)) { // Is value? - val = 0; - if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); - _cimg_mp_return(0); - } - - } - break; - - case 'l' : - if (!std::strncmp(ss,"log(",4)) { // Natural logarithm - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); - _cimg_mp_scalar1(mp_log,arg1); - } - - if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm - arg1 = compile(ss5,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); - _cimg_mp_scalar1(mp_log2,arg1); - } - - if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm - arg1 = compile(ss6,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); - _cimg_mp_scalar1(mp_log10,arg1); - } - break; - - case 'm' : - if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication - s_op = "Function 'mul()'"; - s1 = ss4; while (s1::%s: %s: Sizes of first and second arguments ('%s' and '%s') " - "do not match for third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - pos = vector(arg4*p3); - CImg::vector((uptrT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'n' : - if (!std::strncmp(ss,"narg(",5)) { // Number of arguments - if (*ss5==')') _cimg_mp_return(0); - arg1 = 0; - for (s = ss5; s::vector((uptrT)mp_norm0,pos).move_to(_opcode); break; - case 1 : CImg::vector((uptrT)mp_norm1,pos).move_to(_opcode); break; - case 2 : CImg::vector((uptrT)mp_norm2,pos).move_to(_opcode); break; - case ~0U : CImg::vector((uptrT)mp_norminf,pos).move_to(_opcode); break; - default : - CImg::vector((uptrT)mp_normp,pos,(uptrT)(arg1==~0U?-1:(int)arg1)). - move_to(_opcode); - } - for (s = std::strchr(ss5,'(') + 1; s::sequence((uptrT)_cimg_mp_vector_size(arg2),arg2 + 1, - arg2 + (uptrT)_cimg_mp_vector_size(arg2)). - move_to(_opcode); - else CImg::vector(arg2).move_to(_opcode); - s = ns; - } - (_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'p' : - if (!std::strncmp(ss,"print(",6)) { // Print expression - pos = compile(ss6,se1,depth1,p_ref); - *se1 = 0; - if (_cimg_mp_is_vector(pos)) // Vector - ((CImg::vector((uptrT)mp_vector_print,pos,(uptrT)_cimg_mp_vector_size(pos)), - CImg::string(ss6).unroll('y'))>'y').move_to(code); - else // Scalar - ((CImg::vector((uptrT)mp_print,pos), - CImg::string(ss6).unroll('y'))>'y').move_to(code); - *se1 = ')'; - _cimg_mp_return(pos); - } - break; - - case 'r' : - if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation - s1 = ss4; while (s1::vector((uptrT)mp_rot,pos,arg1,arg2,arg3,arg4).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"round(",6)) { // Value rounding - s_op = "Function 'round()'"; - s1 = ss6; while (s1::%s: %s: Sizes of first and second arguments ('%s' and '%s') " - "do not match for third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - pos = vector(arg4*p3); - CImg::vector((uptrT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"sort(",5)) { // Sort vector - s_op = "Function 'sort()'"; - s1 = ss6; while (s1::vector((uptrT)mp_sort,pos,arg1,p1,arg2).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"sqr(",4)) { // Square - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); - _cimg_mp_scalar1(mp_sqr,arg1); - } - - if (!std::strncmp(ss,"sqrt(",5)) { // Square root - arg1 = compile(ss5,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); - _cimg_mp_scalar1(mp_sqrt,arg1); - } - break; - - case 't' : - if (!std::strncmp(ss,"tan(",4)) { // Tangent - arg1 = compile(ss4,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); - _cimg_mp_scalar1(mp_tan,arg1); - } - - if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent - arg1 = compile(ss5,se1,depth1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); - _cimg_mp_scalar1(mp_tanh,arg1); - } - - if (!std::strncmp(ss,"trace(",6)) { // Matrix trace - arg1 = compile(ss6,se1,depth1,0); - _cimg_mp_check_matrix_square(arg1,1,"Function 'trace()'"); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); - _cimg_mp_scalar2(mp_trace,arg1,p1); - } - - if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose - s_op = "Function 'transp()'"; - s1 = ss7; while (s1::%s: %s: Size of first argument ('%s') does not match" - "for second specified argument 'nb_cols=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - pos = vector(p3*p2); - CImg::vector((uptrT)mp_transp,pos,arg1,p2,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'u' : - if (*ss1=='(') { // Random value with uniform distribution - if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); - s1 = ss2; while (s10) || - !std::strncmp(ss,"vector(",7)) { // Vector - arg2 = 0; // Number of specified values. - s = std::strchr(ss6,'(') + 1; - if (*s!=')' || arg1==~0U) for (; s::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(_opcode); - arg2+=arg4; - } else { CImg::vector(arg3).move_to(_opcode); ++arg2; } - s = ns; - } - if (arg1==~0U) arg1 = arg2; - _cimg_mp_check_vector0(arg1,"Function 'vector()'"); - pos = vector(arg1); - _opcode.insert(CImg::vector((uptrT)mp_vector_init,pos,arg1),0); - (_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'w' : - if (!std::strncmp(ss,"whiledo",7) && (*ss7=='(' || (*ss7 && *ss7<=' ' && *ss8=='('))) { // While...do - if (*ss7<=' ') cimg::swap(*ss7,*ss8); // Allow space before opening brace - s1 = ss8; while (s1::vector((uptrT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2).move_to(code,p1); - _cimg_mp_return(pos); - } - break; - } - - if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || - !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || - !std::strncmp(ss,"arg(",4) || !std::strncmp(ss,"sum(",4) || - !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) || - !std::strncmp(ss,"prod(",5) || !std::strncmp(ss,"mean(",5) || - !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions - pos = scalar(); - CImg::vector((uptrT)(*ss=='a'?(ss[3]=='('?mp_arg:ss[4]=='i'?mp_argmin:mp_argmax): - *ss=='s'?(ss[1]=='u'?mp_sum:mp_std): - *ss=='k'?mp_kth: - *ss=='p'?mp_prod: - *ss=='v'?mp_var: - ss[1]=='i'?mp_min: - ss[1]=='a'?mp_max: - ss[2]=='a'?mp_mean: - mp_med),pos). - move_to(_opcode); - for (s = std::strchr(ss,'(') + 1; s::sequence((uptrT)_cimg_mp_vector_size(arg2),arg2 + 1, - arg2 + (uptrT)_cimg_mp_vector_size(arg2)). - move_to(_opcode); - else CImg::vector(arg2).move_to(_opcode); - s = ns; - } - (_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - - // No corresponding built-in function -> Look for a user-defined macro. - s0 = strchr(ss,'('); - if (s0) { - variable_name.assign(ss,s0 - ss + 1).back() = 0; - cimglist_for(function_def,l) if (!std::strcmp(function_def[l],variable_name)) { - p2 = (unsigned int)function_def[l].back(); // Number of required arguments - CImg _expr = function_body[l]; // Expression to be substituted - p1 = 1; // Indice of current parsed argument - for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments - while (*s && *s<=' ') ++s; - if (*s==')' && p1==1) break; // Function has no arguments - if (p1>p2) { ++p1; break; } - ns = s; while (ns::%s: function '%s()': Number of specified arguments does not " - "match function declaration (%u argument%s required), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,variable_name._data, - p2,p2!=1?"s":"", - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - // Recompute 'pexpr' and 'level' for evaluating substituted expression. - CImg _pexpr(_expr._width); - ns = _pexpr._data; - for (ps = _expr._data, c1 = ' '; *ps; ++ps) { - if (*ps!=' ') c1 = *ps; - *(ns++) = c1; - } - *ns = 0; - - CImg _level(_expr._width - 1); - unsigned int *pd = _level._data; - nb = 0; - for (ps = _expr._data; *ps && nb>=0; ++ps) - *(pd++) = (unsigned int)(*ps=='('||*ps=='['?nb++:*ps==')'||*ps==']'?--nb:nb); - - expr.swap(_expr); pexpr.swap(_pexpr); level.swap(_level); - s0 = user_function; - user_function = function_def[l]; - pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref); - user_function = s0; - expr.swap(_expr); pexpr.swap(_pexpr); level.swap(_level); - _cimg_mp_return(pos); - } - } - } // if (se1==')') - - // Vector specification using initializer '[ ... ]'. - if (*ss=='[' && *se1==']') { - arg1 = 0; // Number of specified values. - if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(_opcode); - arg1+=arg3; - } else { CImg::vector(arg2).move_to(_opcode); ++arg1; } - s = ns; - } - _cimg_mp_check_vector0(arg1,"operator '[]'"); - pos = vector(arg1); - _opcode.insert(CImg::vector((uptrT)mp_vector_init,pos,arg1),0); - (_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - - // Variables related to the input list of images. - if (*ss1=='#' && ss2::vector(listin[p1].median()).move_to(list_median[p1]); - _cimg_mp_constant(*list_median[p1]); - } - _cimg_mp_scalar1(mp_list_median,arg1); - } - if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,*ss1 - '0', - reserved_label[29],reserved_label[30]); - } - switch (*ss1) { - case 'm' : arg2 = 0; break; // im#ind - case 'M' : arg2 = 1; break; // iM#ind - case 'a' : arg2 = 2; break; // ia#ind - case 'v' : arg2 = 3; break; // iv#ind - case 's' : arg2 = 12; break; // is#ind - case 'p' : arg2 = 13; break; // ip#ind - } - } else if (*ss1=='m') switch (*ss) { - case 'x' : arg2 = 4; break; // xm#ind - case 'y' : arg2 = 5; break; // ym#ind - case 'z' : arg2 = 6; break; // zm#ind - case 'c' : arg2 = 7; break; // cm#ind - } else if (*ss1=='M') switch (*ss) { - case 'x' : arg2 = 8; break; // xM#ind - case 'y' : arg2 = 9; break; // yM#ind - case 'z' : arg2 = 10; break; // zM#ind - case 'c' : arg2 = 11; break; // cM#ind - } - if (arg2!=~0U) { - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_stats) list_stats.assign(listin._width); - if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); - _cimg_mp_constant(list_stats(p1,arg2)); - } - _cimg_mp_scalar2(mp_list_stats,arg1,arg2); - } - } - - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4 error. - is_sth = true; // is_valid_variable_name - if (*variable_name>='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name._data + 1; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); - if (is_sth) - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - s0 = std::strchr(ss,'('); - if (s0 && *se1==')') s_op = "function call"; else s_op = "item"; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - s_op,variable_name._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - - // Evaluation procedure. - double operator()(const double x, const double y, const double z, const double c) { - mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c; - for (p_code = p_code_begin; p_code &op = *p_code; - opcode._data = op._data; opcode._height = op._height; - const uptrT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - return *result; - } - - // Evaluation procedure (return output values in vector 'output'). - template - void operator()(const double x, const double y, const double z, const double c, t *const output) { - mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c; - for (p_code = p_code_begin; p_code &op = *p_code; - opcode._data = op._data; opcode._height = op._height; - const uptrT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - if (result_dim) { - const double *ptrs = result + 1; - t *ptrd = output; - for (unsigned int k = 0; k s_type(const unsigned int arg) const { - CImg res; - if (_cimg_mp_is_vector(arg)) { // Vector - CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); - std::sprintf(res._data + 6,"%u",_cimg_mp_vector_size(arg)); - } else CImg::string("scalar").move_to(res); - return res; - } - - // Insert constant value in memory. - unsigned int constant(const double val) { - if (val==(double)(int)val) { - if (val>=0 && val<=9) return (unsigned int)val; - if (val<0 && val>=-5) return (unsigned int)(10 - val); - } - if (val==0.5) return 16; - if (cimg::type::is_nan(val)) return 28; - if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } - const unsigned int pos = mempos++; - mem[pos] = val; - memtype[pos] = 1; // Set constant property - return pos; - } - - // Insert code instructions for processing scalars. - unsigned int scalar() { // Insert new scalar in memory. - if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } - return mempos++; - } - - unsigned int scalar0(const mp_func op) { - const unsigned int pos = scalar(); - CImg::vector((uptrT)op,pos).move_to(code); - return pos; - } - - unsigned int scalar1(const mp_func op, const unsigned int arg1) { - const unsigned int pos = - arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1:scalar(); - CImg::vector((uptrT)op,pos,arg1).move_to(code); - return pos; - } - - unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int pos = - arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: - arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2:scalar(); - CImg::vector((uptrT)op,pos,arg1,arg2).move_to(code); - return pos; - } - - unsigned int scalar3(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { - const unsigned int pos = - arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: - arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: - arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3:scalar(); - CImg::vector((uptrT)op,pos,arg1,arg2,arg3).move_to(code); - return pos; - } - - unsigned int scalar6(const mp_func 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) { - const unsigned int pos = - arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: - arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: - arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3: - arg4>_cimg_mp_c && _cimg_mp_is_temp(arg4)?arg4: - arg5>_cimg_mp_c && _cimg_mp_is_temp(arg5)?arg5: - arg6>_cimg_mp_c && _cimg_mp_is_temp(arg6)?arg6:scalar(); - CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); - return pos; - } - - unsigned int scalar7(const mp_func 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, - const unsigned int arg7) { - const unsigned int pos = - arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: - arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: - arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3: - arg4>_cimg_mp_c && _cimg_mp_is_temp(arg4)?arg4: - arg5>_cimg_mp_c && _cimg_mp_is_temp(arg5)?arg5: - arg6>_cimg_mp_c && _cimg_mp_is_temp(arg6)?arg6: - arg7>_cimg_mp_c && _cimg_mp_is_temp(arg7)?arg7:scalar(); - CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); - return pos; - } - - // Return a string that defines the calling function + the user-defined function scope. - CImg calling_function_s() const { - CImg res; - const unsigned int - l1 = calling_function?std::strlen(calling_function):0, - l2 = user_function?std::strlen(user_function):0; - if (l2) { - res.assign(l1 + l2 + 48); - cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_function); - } else { - res.assign(l1 + l2 + 4); - cimg_snprintf(res,res._width,"%s()",calling_function); - } - return res; - } - - // Return true if specified argument can be a part of an allowed variable name. - bool is_varchar(const char c) const { - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; - } - - // Insert code instructions for processing vectors. - bool is_tmp_vector(const unsigned int arg) const { - unsigned int siz = _cimg_mp_vector_size(arg); - if (siz>8) return false; - const int *ptr = memtype.data(arg + 1); - bool is_tmp = true; - while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } - return is_tmp; - } - - void set_variable_vector(const unsigned int arg) { - unsigned int siz = _cimg_mp_vector_size(arg); - int *ptr = memtype.data(arg + 1); - while (siz-->0) *(ptr++) = -1; - } - - unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory. - if (mempos + siz>=mem._width) { - mem.resize(2*mem._width + siz,1,1,1,0); - memtype.resize(mem._width,1,1,1,0); - } - const unsigned int pos = mempos++; - mem[pos] = cimg::type::nan(); - memtype[pos] = siz + 1; - mempos+=siz; - return pos; - } - - unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory. - const unsigned int - siz = _cimg_mp_vector_size(arg), - pos = vector(siz); - CImg::vector((uptrT)mp_vector_copy,pos,arg,siz).move_to(code); - return pos; - } - - void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_vector_size(pos); - if (siz>24) CImg::vector((uptrT)mp_self_map_vector_s,pos,siz,(uptrT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]); - } - } - - void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_vector_size(pos); - if (siz>24) CImg::vector((uptrT)mp_self_map_vector_v,pos,siz,(uptrT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); - } - } - - unsigned int vector1_v(const mp_func op, const unsigned int arg1) { - const unsigned int - siz = _cimg_mp_vector_size(arg1), - pos = is_tmp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((uptrT)mp_vector_map_v,pos,siz,(uptrT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_vector_size(arg1), - pos = is_tmp_vector(arg1)?arg1:is_tmp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg::vector((uptrT)mp_vector_map_vv,pos,siz,(uptrT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_vector_size(arg1), - pos = is_tmp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((uptrT)mp_vector_map_vs,pos,siz,(uptrT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_vector_size(arg2), - pos = is_tmp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg::vector((uptrT)mp_vector_map_sv,pos,siz,(uptrT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, - const unsigned int arg3) { - const unsigned int - siz = _cimg_mp_vector_size(arg1), - pos = is_tmp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((uptrT)mp_vector_map_vss,pos,siz,(uptrT)op,arg1,arg2,arg3).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg::vector((uptrT)op,pos + k,arg1,arg2,arg3).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - // Check if a memory slot is a positive integer constant scalar value. - void check_constant(const unsigned int arg, const unsigned int n_arg, const char *const s_op, - const bool is_strictly_positive, - const char *const ss, char *const se, const char saved_char) { - _cimg_mp_check_type(arg,n_arg,s_op,1,0); - if (!_cimg_mp_is_constant(arg) || mem[arg]<(is_strictly_positive?1:0) || (double)(int)mem[arg]!=mem[arg]) { - const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ": - n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth ": - n_arg==9?"Ninth ":"One "; - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): %s: %s%s (of type '%s') is not a %spositive integer constant, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_arg,*s_arg?"argument":"Argument",s_type(arg)._data, - is_strictly_positive?"strictly ":"", - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - } - - // Check a matrix is square. - void check_matrix_square(const unsigned int arg, const unsigned int n_arg, const char *const s_op, - const char *const ss, char *const se, const char saved_char) { - _cimg_mp_check_type(arg,n_arg,s_op,2,0); - const unsigned int - siz = _cimg_mp_vector_size(arg), - n = (unsigned int)std::sqrt((float)siz); - if (n*n!=siz) { - const char *s_arg; - if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; - else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): %s: %s%s (of type '%s') " - "cannot be considered as a square matrix, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), - s_type(arg)._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - } - - // Check type compatibility for one argument. - // Bits of 'mode' tells what types are allowed: - // { 1 = scalar | 2 = vectorN }. - // If 'N' is not zero, it also restricts the vectors to be of size N only. - void check_type(const unsigned int arg, const unsigned int n_arg, const char *const s_op, - const unsigned int mode, const unsigned int N, - const char *const ss, char *const se, const char saved_char) { - const bool - is_scalar = _cimg_mp_is_scalar(arg), - is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_vector_size(arg)==N); - bool cond = false; - if (mode&1) cond|=is_scalar; - if (mode&2) cond|=is_vector; - if (!cond) { - const char *s_arg; - if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; - else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; - CImg sb_type(32); - if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); - else if (mode==2) { - if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); - else cimg_snprintf(sb_type,sb_type._width,"'vector'"); - } else { - if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); - else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); - } - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): %s: %s%s has invalid type '%s' (should be %s), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), - s_type(arg)._data,sb_type._data, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - } - - // Check a vector is not 0-dimensional, or with unknown dimension at compile time. - void check_vector0(const unsigned int dim, const char *const s_op, - const char *const ss, char *const se, const char saved_char) { - if (!dim) { - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): %s: Invalid construction of a 0-dimensional vector, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } else if (dim==~0U) { - *se = saved_char; cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): %s: Invalid construction of a vector with dynamic size, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - } - - // Evaluation functions, known by the parser. - // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(uptrT), - // so we can store pointers to them directly in the opcode vectors. -#ifdef _mp_arg -#undef _mp_arg -#endif -#define _mp_arg(x) mp.mem[mp.opcode[x]] - - static double mp_abs(_cimg_math_parser& mp) { - return cimg::abs(_mp_arg(2)); - } - - static double mp_add(_cimg_math_parser& mp) { - return _mp_arg(2) + _mp_arg(3); - } - - static double mp_acos(_cimg_math_parser& mp) { - return std::acos(_mp_arg(2)); - } - - static double mp_arg(_cimg_math_parser& mp) { - const int _ind = (int)_mp_arg(2); - const unsigned int nb_args = mp.opcode._height - 2, ind = _ind<0?_ind + nb_args:(unsigned int)_ind; - if (ind>=nb_args) return 0; - return _mp_arg(ind + 2); - } - - static double mp_argmin(_cimg_math_parser& mp) { - double val = _mp_arg(2); - unsigned int argval = 0; - for (unsigned int i = 3; ival) { val = _val; argval = i - 2; } - } - return (double)argval; - } - - static double mp_asin(_cimg_math_parser& mp) { - return std::asin(_mp_arg(2)); - } - - static double mp_atan(_cimg_math_parser& mp) { - return std::atan(_mp_arg(2)); - } - - static double mp_atan2(_cimg_math_parser& mp) { - return std::atan2(_mp_arg(2),_mp_arg(3)); - } - - static double mp_bitwise_and(_cimg_math_parser& mp) { - return (double)((unsigned long)_mp_arg(2) & (unsigned long)_mp_arg(3)); - } - - static double mp_bitwise_left_shift(_cimg_math_parser& mp) { - return (double)((long)_mp_arg(2)<<(unsigned int)_mp_arg(3)); - } - - static double mp_bitwise_not(_cimg_math_parser& mp) { - return (double)~(unsigned long)_mp_arg(2); - } - - static double mp_bitwise_or(_cimg_math_parser& mp) { - return (double)((unsigned long)_mp_arg(2) | (unsigned long)_mp_arg(3)); - } - - static double mp_bitwise_right_shift(_cimg_math_parser& mp) { - return (double)((long)_mp_arg(2)>>(unsigned int)_mp_arg(3)); - } - - static double mp_cbrt(_cimg_math_parser& mp) { - return std::pow(_mp_arg(2),1.0/3); - } - - static double mp_complex_conj(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - double *ptrd = &_mp_arg(1) + 1; - *(ptrd++) = *(ptrs++); - *ptrd = -*(ptrs); - return cimg::type::nan(); - } - - static double mp_complex_div_sv(_cimg_math_parser& mp) { - const double - *ptr2 = &_mp_arg(3) + 1, - r1 = _mp_arg(2), - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - const double denom = r2*r2 + i2*i2; - *(ptrd++) = r1*r2/denom; - *ptrd = -r1*i2/denom; - return cimg::type::nan(); - } - - static double mp_complex_div_vv(_cimg_math_parser& mp) { - const double - *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, - r1 = *(ptr1++), i1 = *ptr1, - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - const double denom = r2*r2 + i2*i2; - *(ptrd++) = (r1*r2 + i1*i2)/denom; - *ptrd = (r2*i1 - r1*i2)/denom; - return cimg::type::nan(); - } - - static double mp_complex_exp(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r); - *(ptrd++) = er*std::cos(i); - *(ptrd++) = er*std::sin(i); - return cimg::type::nan(); - } - - static double mp_complex_log(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs); - *(ptrd++) = std::log(std::sqrt(r*r + i*i)); - *(ptrd++) = std::atan2(i,r); - return cimg::type::nan(); - } - - static double mp_complex_mul(_cimg_math_parser& mp) { - const double - *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, - r1 = *(ptr1++), i1 = *ptr1, - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - *(ptrd++) = r1*r2 - i1*i2; - *(ptrd++) = r1*i2 + r2*i1; - return cimg::type::nan(); - } - - static void _mp_complex_pow(const double r1, const double i1, - const double r2, const double i2, - double *ptrd) { - double ro, io; - if (cimg::abs(i2)<1e-15) { // Exponent is real - if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { - if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } - else ro = io = 0; - } else { - const double - mod1_2 = r1*r1 + i1*i1, - phi1 = std::atan2(i1,r1), - modo = std::pow(mod1_2,0.5*r2), - phio = r2*phi1; - ro = modo*std::cos(phio); - io = modo*std::sin(phio); - } - } else { // Exponent is complex - if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; - const double - mod1_2 = r1*r1 + i1*i1, - phi1 = std::atan2(i1,r1), - modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), - phio = r2*phi1 + 0.5*i2*std::log(mod1_2); - ro = modo*std::cos(phio); - io = modo*std::sin(phio); - } - *(ptrd++) = ro; - *ptrd = io; - } - - static double mp_complex_pow_sv(_cimg_math_parser& mp) { - const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); - return cimg::type::nan(); - } - - static double mp_complex_pow_vs(_cimg_math_parser& mp) { - const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); - return cimg::type::nan(); - } - - static double mp_complex_pow_vv(_cimg_math_parser& mp) { - const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); - return cimg::type::nan(); - } - - static double mp_cos(_cimg_math_parser& mp) { - return std::cos(_mp_arg(2)); - } - - static double mp_cosh(_cimg_math_parser& mp) { - return std::cosh(_mp_arg(2)); - } - - static double mp_crop(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const bool boundary_conditions = (bool)_mp_arg(10); - const int - x = (int)_mp_arg(2), - y = (int)_mp_arg(3), - z = (int)_mp_arg(4), - c = (int)_mp_arg(5), - dx = (int)mp.opcode[6], - dy = (int)mp.opcode[7], - dz = (int)mp.opcode[8], - dc = (int)mp.opcode[9]; - const CImg &img = mp.imgin; - if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); - else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, - x + dx - 1,y + dy - 1,z + dz - 1,c + dc - 1, - boundary_conditions); - return cimg::type::nan(); - } - - static double mp_cross(_cimg_math_parser& mp) { - CImg - vout(&_mp_arg(1) + 1,1,3,1,1,true), - v1(&_mp_arg(2) + 1,1,3,1,1,true), - v2(&_mp_arg(3) + 1,1,3,1,1,true); - (vout = v1).cross(v2); - return cimg::type::nan(); - } - - static double mp_cut(_cimg_math_parser& mp) { - double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); - return valcmax?cmax:val; - } - - static double mp_debug(_cimg_math_parser& mp) { -#ifdef cimg_use_openmp - const unsigned int n_thread = omp_get_thread_num(); -#else - const unsigned int n_thread = 0; -#endif - CImg expr(mp.opcode._height - 3); - const uptrT *ptrs = mp.opcode._data + 3; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - const uptrT g_target = mp.opcode[1]; - std::fprintf(cimg::output(), - "\n[_cimg_math_parser] %p[thread #%u]:%*c" - "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", - (void*)&mp,n_thread,mp.debug_indent,' ', - expr._data,(unsigned int)mp.opcode[2],(unsigned int)g_target,mp.mem._width); - std::fflush(cimg::output()); - const CImg *const p_end = (++mp.p_code) + mp.opcode[2]; - CImg _op; - mp.debug_indent+=3; - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - - _op.assign(1,op._height - 1); - const uptrT *ptrs = op._data + 1; - for (uptrT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd mem[%u] = %g", - (void*)&mp,n_thread,mp.debug_indent,' ', - (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), - (unsigned int)target,mp.mem[target]); - std::fflush(cimg::output()); - } - mp.debug_indent-=3; - std::fprintf(cimg::output(), - "\n[_cimg_math_parser] %p[thread #%u]:%*c" - "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)", - (void*)&mp,n_thread,mp.debug_indent,' ', - expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); - std::fflush(cimg::output()); - --mp.p_code; - return mp.mem[g_target]; - } - - static double mp_decrement(_cimg_math_parser& mp) { - return _mp_arg(2) - 1; - } - - static double mp_det(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode(3); - return CImg(ptrs,k,k,1,1,true).det(); - } - - static double mp_diag(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode(3); - CImg(ptrd,k,k,1,1,true) = CImg(ptrs,1,k,1,1,true).get_diagonal(); - return cimg::type::nan(); - } - - static double mp_dot(_cimg_math_parser& mp) { - const unsigned int siz = (unsigned int)mp.opcode[4]; - return CImg(&_mp_arg(2) + 1,1,siz,1,1,true). - dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); - } - - static double mp_dowhile(_cimg_math_parser& mp) { - const uptrT - mem_proc = mp.opcode[1], - mem_cond = mp.opcode[2]; - const CImg - *const p_proc = ++mp.p_code, - *const p_end = p_proc + mp.opcode[3]; - do { - for (mp.p_code = p_proc; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - } while (mp.mem[mem_cond]); - --mp.p_code; - return mp.mem[mem_proc]; - } - - static double mp_div(_cimg_math_parser& mp) { - return _mp_arg(2)/_mp_arg(3); - } - - static double mp_eig(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode(3); - CImg val, vec; - CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); - CImg(ptrd,k,1,1,1,true) = val.unroll('x'); - CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); - return cimg::type::nan(); - } - - static double mp_eq(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)==_mp_arg(3)); - } - - static double mp_exp(_cimg_math_parser& mp) { - return std::exp(_mp_arg(2)); - } - - static double mp_eye(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int k = (unsigned int)mp.opcode(2); - CImg(ptrd,k,k,1,1,true).identity_matrix(); - return cimg::type::nan(); - } - - static double mp_g(_cimg_math_parser& mp) { - cimg::unused(mp); - return cimg::grand(); - } - - static double mp_gauss(_cimg_math_parser& mp) { - const double x = _mp_arg(2), s = _mp_arg(3); - return std::exp(-x*x/(2*s*s))/std::sqrt(2*s*s*cimg::PI); - } - - static double mp_gt(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)>_mp_arg(3)); - } - - static double mp_gte(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)>=_mp_arg(3)); - } - - static double mp_hypot(_cimg_math_parser& mp) { - return cimg::hypot(_mp_arg(2),_mp_arg(3)); - } - - static double mp_i(_cimg_math_parser& mp) { - return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_x],(int)mp.mem[_cimg_mp_y], - (int)mp.mem[_cimg_mp_z],(int)mp.mem[_cimg_mp_c],0); - } - - static double mp_if(_cimg_math_parser& mp) { - const bool is_cond = (bool)_mp_arg(2); - const uptrT - mem_left = mp.opcode[3], - mem_right = mp.opcode[4]; - const CImg - *const p_right = ++mp.p_code + mp.opcode[5], - *const p_end = p_right + mp.opcode[6]; - const unsigned int vtarget = mp.opcode[1], vsiz = mp.opcode[7]; - if (is_cond) { - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - mp.p_code = p_end - 1; - if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[mem_left] + 1,sizeof(double)*vsiz); - return mp.mem[mem_left]; - } - for (mp.p_code = p_right; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[mem_right] + 1,sizeof(double)*vsiz); - return mp.mem[mem_right]; - } - - static double mp_increment(_cimg_math_parser& mp) { - return _mp_arg(2) + 1; - } - - static double mp_int(_cimg_math_parser& mp) { - return (double)(long)_mp_arg(2); - } - - static double mp_inv(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode(3); - CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(); - return cimg::type::nan(); - } - - static double mp_ioff(_cimg_math_parser& mp) { - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const CImg &img = mp.imgin; - const long - off = (long)_mp_arg(2), - whds = (long)img.size(); - if (off<0 || off>=whds) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (img) return (double)img[cimg::mod(off,whds)]; - return 0; - case 1 : // Neumann boundary - if (img) return (double)(off<0?*img:img.back()); - return 0; - default : // Dirichet boundary - return 0; - } - return (double)img[off]; - } - - static double mp_isbool(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return (double)(val==0.0 || val==1.0); - } - - static double mp_isin(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - for (unsigned int i = 3; i::is_inf(_mp_arg(2)); - } - - static double mp_isint(_cimg_math_parser& mp) { - return (double)(cimg::mod(_mp_arg(2),1.0)==0); - } - - static double mp_isnan(_cimg_math_parser& mp) { - return (double)cimg::type::is_nan(_mp_arg(2)); - } - - static double mp_ixyzc(_cimg_math_parser& mp) { - const unsigned int - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg &img = mp.imgin; - const double - x = _mp_arg(2), y = _mp_arg(3), - z = _mp_arg(4), c = _mp_arg(5); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - return (double)img.atXYZC(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - if (boundary_conditions==1) - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - if (boundary_conditions==1) - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); - } - } - - static double mp_joff(_cimg_math_parser& mp) { - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const CImg &img = mp.imgin; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), - whds = (long)img.size(); - if (off<0 || off>=whds) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (img) return (double)img[cimg::mod(off,whds)]; - return 0; - case 1 : // Neumann boundary - if (img) return (double)(off<0?*img:img.back()); - return 0; - default : // Dirichet boundary - return 0; - } - return (double)img[off]; - } - - static double mp_jxyzc(_cimg_math_parser& mp) { - const unsigned int - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg &img = mp.imgin; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], - oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c], - x = ox + _mp_arg(2), y = oy + _mp_arg(3), - z = oz + _mp_arg(4), c = oc + _mp_arg(5); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - return (double)img.atXYZC(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - if (boundary_conditions==1) - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - if (boundary_conditions==1) - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); - } - } - - static double mp_kth(_cimg_math_parser& mp) { - CImg vals(mp.opcode._height - 3); - double *p = vals.data(); - for (unsigned int i = 3; i &img = mp.listin[ind]; - if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); - else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, - x + dx - 1,y + dy - 1, - z + dz - 1,c + dc - 1, - boundary_conditions); - return cimg::type::nan(); - } - - static double mp_list_depth(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._depth; - } - - static double mp_list_height(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._height; - } - - static double mp_list_ioff(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const CImg &img = mp.listin[ind]; - const long - off = (long)_mp_arg(3), - whds = (long)img.size(); - if (off<0 || off>=whds) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (img) return (double)img[cimg::mod(off,whds)]; - return 0; - case 1 : // Neumann boundary - if (img) return (double)(off<0?*img:img.back()); - return 0; - default : // Dirichet boundary - return 0; - } - return (double)img[off]; - } - - static double mp_list_is_shared(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._is_shared; - } - - static double mp_list_ixyzc(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(7), - boundary_conditions = (unsigned int)_mp_arg(8); - const CImg &img = mp.listin[ind]; - const double - x = _mp_arg(3), y = _mp_arg(4), - z = _mp_arg(5), c = _mp_arg(6); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - return (double)img.atXYZC(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - if (boundary_conditions==1) - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - if (boundary_conditions==1) - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); - } - } - - static double mp_list_joff(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const CImg &img = mp.listin[ind]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), - whds = (long)img.size(); - if (off<0 || off>=whds) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (img) return (double)img(ind,cimg::mod(off,whds)); - return 0; - case 1 : // Neumann boundary - if (img) return (double)(off<0?*img:img.back()); - return 0; - default : // Dirichet boundary - return 0; - } - return (double)img[off]; - } - - static double mp_list_jxyzc(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(7), - boundary_conditions = (unsigned int)_mp_arg(8); - const CImg &img = mp.listin[ind]; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], - oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c], - x = ox + _mp_arg(3), y = oy + _mp_arg(4), - z = oz + _mp_arg(5), c = oc + _mp_arg(6); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - return (double)img.atXYZC(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - if (boundary_conditions==1) - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - if (boundary_conditions==1) - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); - } - } - - static double mp_list_median(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - if (!mp.list_median) mp.list_median.assign(mp.listin._width); - if (!mp.list_median[ind]) CImg::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); - return *mp.list_median[ind]; - } - - static double mp_list_set_ioff(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; - const long - off = (long)_mp_arg(3), - whds = (long)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off &img = mp.listout[ind]; - const int - x = (int)_mp_arg(3), y = (int)_mp_arg(4), - z = (int)_mp_arg(5), c = (int)_mp_arg(6); - const double val = _mp_arg(1); - if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), - whds = (long)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off &img = mp.listout[ind]; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], - oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c]; - const int - x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), - z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); - const double val = _mp_arg(1); - if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; - const long - off = (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off &img = mp.listout[ind]; - const long - off = (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off::nan(); - } - - static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; - const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5); - const T val = (T)_mp_arg(1); - if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; - const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x=0 && y=0 && z::nan(); - } - - static double mp_list_set_Joff_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off::nan(); - } - - static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; - const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; - const int x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), z = (int)(oz + _mp_arg(5)); - const T val = (T)_mp_arg(1); - if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; - const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; - const int x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), z = (int)(oz + _mp_arg(5)); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x=0 && y=0 && z::nan(); - } - - static double mp_list_spectrum(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._spectrum; - } - - static double mp_list_stats(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - k = (unsigned int)_mp_arg(3); - if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); - if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); - return mp.list_stats(ind,k); - } - - static double mp_list_wh(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height; - } - - static double mp_list_whd(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; - } - - static double mp_list_whds(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; - } - - static double mp_list_width(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width; - } - - static double mp_list_Ioff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const CImg &img = mp.listin[ind]; - const long - off = (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const T *ptrs; - if (off<0 || off>=whd) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (!img) { - ptrs = &img[cimg::mod(off,whd)]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - case 1 : // Neumann boundary - if (img) { - ptrs = off<0?img._data:&img.back(); - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - default : // Dirichet boundary - std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - } - ptrs = &img[off]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type::nan(); - } - - static double mp_list_Ixyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg &img = mp.listin[ind]; - const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()),c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); - } - return cimg::type::nan(); - } - - static double mp_list_Joff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], oz = (int)mp.mem[_cimg_mp_z]; - const CImg &img = mp.listin[ind]; - const long - off = img.offset(ox,oy,oz) + (long)_mp_arg(3), - whd = img.width()*img.height()*img.depth(); - const T *ptrs; - if (off<0 || off>=whd) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (!img) { - ptrs = &img[cimg::mod(off,whd)]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - case 1 : // Neumann boundary - if (img) { - ptrs = off<0?img._data:&img.back(); - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - default : // Dirichet boundary - std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - } - ptrs = &img[off]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type::nan(); - } - - static double mp_list_Jxyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg &img = mp.listin[ind]; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z], - x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()),c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); - } - return cimg::type::nan(); - } - - static double mp_log(_cimg_math_parser& mp) { - return std::log(_mp_arg(2)); - } - - static double mp_log10(_cimg_math_parser& mp) { - return std::log10(_mp_arg(2)); - } - - static double mp_log2(_cimg_math_parser& mp) { - return cimg::log2(_mp_arg(2)); - } - - static double mp_logical_and(_cimg_math_parser& mp) { - const bool val_left = (bool)_mp_arg(2); - const CImg *const p_end = ++mp.p_code + mp.opcode[4]; - if (!val_left) { mp.p_code = p_end - 1; return 0; } - const uptrT mem_right = mp.opcode[3]; - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_right]; - } - - static double mp_logical_not(_cimg_math_parser& mp) { - return (double)!_mp_arg(2); - } - - static double mp_logical_or(_cimg_math_parser& mp) { - const bool val_left = (bool)_mp_arg(2); - const CImg *const p_end = ++mp.p_code + mp.opcode[4]; - if (val_left) { mp.p_code = p_end - 1; return 1; } - const uptrT mem_right = mp.opcode[3]; - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_right]; - } - - - static double mp_lt(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)<_mp_arg(3)); - } - - static double mp_lte(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)<=_mp_arg(3)); - } - - static double mp_matrix_mul(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double - *ptr1 = &_mp_arg(2) + 1, - *ptr2 = &_mp_arg(3) + 1; - const unsigned int - k = (unsigned int)mp.opcode(4), - l = (unsigned int)mp.opcode(5), - m = (unsigned int)mp.opcode(6); - CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); - return cimg::type::nan(); - } - - static double mp_max(_cimg_math_parser& mp) { - double val = _mp_arg(2); - for (unsigned int i = 3; i=mp.mem.width()) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: 'copy()': " - "Out-of-bounds variable pointer " - "(length: %ld, increment: %ld, offset start: %ld, " - "offset end: %ld, offset max: %u).", - mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1); - return &mp.mem[off]; - } - - static float* _mp_memcopy_float(_cimg_math_parser& mp, const uptrT *const p_ref, - const long siz, const long inc) { - const unsigned ind = p_ref[1]; - const CImg &img = ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]; - const bool is_relative = (bool)p_ref[2]; - int ox, oy, oz, oc; - long off = 0; - if (is_relative) { - ox = (int)mp.mem[_cimg_mp_x]; - oy = (int)mp.mem[_cimg_mp_y]; - oz = (int)mp.mem[_cimg_mp_z]; - oc = (int)mp.mem[_cimg_mp_c]; - off = img.offset(ox,oy,oz,oc); - } - if ((*p_ref)%2) { - const int - x = (int)mp.mem[p_ref[3]], - y = (int)mp.mem[p_ref[4]], - z = (int)mp.mem[p_ref[5]], - c = *p_ref==5?0:(int)mp.mem[p_ref[6]]; - off+=(long)img.offset(x,y,z,c); - } else off+=(long)mp.mem[p_ref[3]]; - const long eoff = off + (siz - 1)*inc; - if (off<0 || eoff>=(long)img.size()) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'copy()': " - "Out-of-bounds image pointer " - "(length: %ld, increment: %ld, offset start: %ld, " - "offset end: %ld, offset max: %lu).", - mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1); - return (float*)&img[off]; - } - - static double mp_memcopy(_cimg_math_parser& mp) { - long siz = (long)_mp_arg(4); - const long inc_d = (long)_mp_arg(5), inc_s = (long)_mp_arg(6); - if (siz>0) { - const bool - is_doubled = mp.opcode[7]<=1, - is_doubles = mp.opcode[14]<=1; - if (is_doubled && is_doubles) { // (double*) <- (double*) - double *ptrd = _mp_memcopy_double(mp,mp.opcode[2],&mp.opcode[7],siz,inc_d); - const double *ptrs = _mp_memcopy_double(mp,mp.opcode[3],&mp.opcode[14],siz,inc_s); - if (inc_d==1 && inc_s==1) { - if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); - else std::memmove(ptrd,ptrs,siz*sizeof(double)); - } else { - if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) - while (siz-->0) { *ptrd = (double)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else { // Overlapping buffers - CImg buf(siz); - cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } - ptrs = buf; - while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } - } - } - } else if (is_doubled && !is_doubles) { // (double*) <- (float*) - double *ptrd = _mp_memcopy_double(mp,mp.opcode[2],&mp.opcode[7],siz,inc_d); - const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[14],siz,inc_s); - while (siz-->0) { *ptrd = (double)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } - } else if (!is_doubled && is_doubles) { // (float*) <- (double*) - float *ptrd = _mp_memcopy_float(mp,&mp.opcode[7],siz,inc_d); - const double *ptrs = _mp_memcopy_double(mp,mp.opcode[3],&mp.opcode[14],siz,inc_s); - while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } - } else { // (float*) <- (float*) - float *ptrd = _mp_memcopy_float(mp,&mp.opcode[7],siz,inc_d); - const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[14],siz,inc_s); - if (inc_d==1 && inc_s==1) { - if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); - else std::memmove(ptrd,ptrs,siz*sizeof(float)); - } else { - if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) - while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else { // Overlapping buffers - CImg buf(siz); - cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; } - ptrs = buf; - while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } - } - } - } - } - return _mp_arg(1); - } - - static double mp_min(_cimg_math_parser& mp) { - double val = _mp_arg(2); - for (unsigned int i = 3; i vals(mp.opcode._height - 2); - double *p = vals.data(); - for (unsigned int i = 2; ires) res = val; - } - return res; - } - - static double mp_normp(_cimg_math_parser& mp) { - const double p = (double)mp.opcode[2]; - double res = 0; - for (unsigned int i = 3; i0?res:0.0; - } - - static double mp_pow(_cimg_math_parser& mp) { - const double v = _mp_arg(2), p = _mp_arg(3); - return std::pow(v,p); - } - - static double mp_pow3(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return val*val*val; - } - - static double mp_pow4(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return val*val*val*val; - } - - static double mp_print(_cimg_math_parser& mp) { - CImg expr(mp.opcode._height - 2); - const uptrT *ptrs = mp.opcode._data + 2; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - const double val = _mp_arg(1); - std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = %g",expr._data,val); - std::fflush(cimg::output()); - return val; - } - - static double mp_prod(_cimg_math_parser& mp) { - double val = _mp_arg(2); - for (unsigned int i = 3; i(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); - return cimg::type::nan(); - } - - static double mp_round(_cimg_math_parser& mp) { - return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); - } - - static double mp_self_add(_cimg_math_parser& mp) { - return _mp_arg(1)+=_mp_arg(2); - } - - static double mp_self_bitwise_and(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((unsigned long)val & (unsigned long)_mp_arg(2)); - } - - static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((long)val<<(unsigned int)_mp_arg(2)); - } - - static double mp_self_bitwise_or(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((unsigned long)val | (unsigned long)_mp_arg(2)); - } - - static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((long)val>>(unsigned int)_mp_arg(2)); - } - - static double mp_self_decrement(_cimg_math_parser& mp) { - return --_mp_arg(1); - } - - static double mp_self_increment(_cimg_math_parser& mp) { - return ++_mp_arg(1); - } - - static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar - unsigned int - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2]; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,3); - l_opcode[2] = mp.opcode[4]; // Scalar argument. - l_opcode.swap(mp.opcode); - uptrT &target = mp.opcode[1]; - while (siz-->0) { target = ptrd++; (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector - unsigned int - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,4); - l_opcode.swap(mp.opcode); - uptrT &target = mp.opcode[1], &argument = mp.opcode[2]; - while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_self_mul(_cimg_math_parser& mp) { - return _mp_arg(1)*=_mp_arg(2); - } - - static double mp_self_div(_cimg_math_parser& mp) { - return _mp_arg(1)/=_mp_arg(2); - } - - static double mp_self_modulo(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = cimg::mod(val,_mp_arg(2)); - } - - static double mp_self_pow(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = std::pow(val,_mp_arg(2)); - } - - static double mp_self_sub(_cimg_math_parser& mp) { - return _mp_arg(1)-=_mp_arg(2); - } - - static double mp_set_ioff(_cimg_math_parser& mp) { - CImg &img = mp.imgout; - const long - off = (long)_mp_arg(2), - whds = (long)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off &img = mp.imgout; - const int - x = (int)_mp_arg(2), y = (int)_mp_arg(3), - z = (int)_mp_arg(4), c = (int)_mp_arg(5); - const double val = _mp_arg(1); - if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), - whds = (long)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off &img = mp.imgout; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], - oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c]; - const int - x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), - z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); - const double val = _mp_arg(1); - if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; - const long - off = (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off &img = mp.imgout; - const long - off = (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off::nan(); - } - - static double mp_set_Ixyz_s(_cimg_math_parser& mp) { - CImg &img = mp.imgout; - const int x = (int)_mp_arg(2), y = (int)_mp_arg(3), z = (int)_mp_arg(4); - const T val = (T)_mp_arg(1); - if (x>=0 && x=0 && y=0 && z &img = mp.imgout; - const int x = (int)_mp_arg(2), y = (int)_mp_arg(3), z = (int)_mp_arg(4); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x=0 && y=0 && z::nan(); - } - - static double mp_set_Joff_s(_cimg_math_parser& mp) { - CImg &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], - oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; - const long - off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off::nan(); - } - - static double mp_set_Jxyz_s(_cimg_math_parser& mp) { - CImg &img = mp.imgout; - const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; - const int x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), z = (int)(oz + _mp_arg(4)); - const T val = (T)_mp_arg(1); - if (x>=0 && x=0 && y=0 && z &img = mp.imgout; - const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; - const int x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), z = (int)(oz + _mp_arg(4)); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x=0 && y=0 && z::nan(); - } - - static double mp_sign(_cimg_math_parser& mp) { - return cimg::sign(_mp_arg(2)); - } - - static double mp_sin(_cimg_math_parser& mp) { - return std::sin(_mp_arg(2)); - } - - static double mp_sinc(_cimg_math_parser& mp) { - return cimg::sinc(_mp_arg(2)); - } - - static double mp_sinh(_cimg_math_parser& mp) { - return std::sinh(_mp_arg(2)); - } - - static double mp_solve(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double - *ptr1 = &_mp_arg(2) + 1, - *ptr2 = &_mp_arg(3) + 1; - const unsigned int - k = (unsigned int)mp.opcode(4), - l = (unsigned int)mp.opcode(5), - m = (unsigned int)mp.opcode(6); - CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,true).get_solve(CImg(ptr1,k,l,1,1,true)); - return cimg::type::nan(); - } - - static double mp_sort(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const unsigned int siz = mp.opcode[3]; - const bool is_increasing = (bool)_mp_arg(4); - CImg(ptrd,1,siz,1,1,true) = CImg(ptrs,1,siz,1,1,true).get_sort(is_increasing); - return cimg::type::nan(); - } - - static double mp_sqr(_cimg_math_parser& mp) { - return cimg::sqr(_mp_arg(2)); - } - - static double mp_sqrt(_cimg_math_parser& mp) { - return std::sqrt(_mp_arg(2)); - } - - static double mp_std(_cimg_math_parser& mp) { - CImg vals(mp.opcode._height - 2); - double *p = vals.data(); - for (unsigned int i = 2; i(ptrs,k,k,1,1,true).trace(); - } - - static double mp_transp(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int - k = (unsigned int)mp.opcode(3), - l = (unsigned int)mp.opcode(4); - CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_transpose(); - return cimg::type::nan(); - } - - static double mp_u(_cimg_math_parser& mp) { - return cimg::rand(_mp_arg(2),_mp_arg(3)); - } - - static double mp_var(_cimg_math_parser& mp) { - CImg vals(mp.opcode._height - 2); - double *p = vals.data(); - for (unsigned int i = 2; i::nan(); - } - - static double mp_vector_crop(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const unsigned int p1 = mp.opcode[3], p2 = mp.opcode[4]; - std::memcpy(ptrd,ptrs + p1,p2*sizeof(double)); - return cimg::type::nan(); - } - - static double mp_vector_init(_cimg_math_parser& mp) { - unsigned int - ptrs = 3U, - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2]; - switch (mp.opcode._height) { - case 3 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given - case 4 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; - default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode._height) ptrs = 3U; } - } - return cimg::type::nan(); - } - - static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[5] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(4); - l_opcode[2] = mp.opcode[4]; // Scalar argument1 - l_opcode.swap(mp.opcode); - uptrT &argument2 = mp.opcode[3]; - while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,3); - l_opcode.swap(mp.opcode); - uptrT &argument = mp.opcode[2]; - while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,4); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode.swap(mp.opcode); - uptrT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,5); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode[4] = mp.opcode[6]; // Scalar argument3 - l_opcode.swap(mp.opcode); - uptrT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs1 = (unsigned int)mp.opcode[4] + 1, - ptrs2 = (unsigned int)mp.opcode[5] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,4); - l_opcode.swap(mp.opcode); - uptrT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; - while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_off(_cimg_math_parser& mp) { - const unsigned int - ptr = mp.opcode[2] + 1, - siz = (int)mp.opcode[3]; - const int off = (int)_mp_arg(4); - return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); - } - - static double mp_vector_set_off(_cimg_math_parser& mp) { - const unsigned int - ptr = mp.opcode[2] + 1, - siz = mp.opcode[3]; - const int off = (int)_mp_arg(4); - if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5); - return _mp_arg(5); - } - - static double mp_vector_print(_cimg_math_parser& mp) { - CImg expr(mp.opcode._height - 3); - const uptrT *ptrs = mp.opcode._data + 3; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - unsigned int - ptr = mp.opcode[1] + 1, - siz = mp.opcode[2]; - std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = [",expr._data); - while (siz-->0) std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":""); - std::fputc(']',cimg::output()); - std::fflush(cimg::output()); - return cimg::type::nan(); - } - - static double mp_whiledo(_cimg_math_parser& mp) { // Used also by 'for()' - const uptrT - mem_proc = mp.opcode[1], - mem_cond = mp.opcode[2]; - const CImg - *const p_cond = ++mp.p_code, - *const p_proc = p_cond + mp.opcode[3], - *const p_end = p_proc + mp.opcode[4]; - const unsigned int vsiz = mp.opcode[5]; - bool is_first_iter = true, is_cond = false; - do { - for (mp.p_code = p_cond; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - is_cond = (bool)mp.mem[mem_cond]; - if (is_cond) { // Evaluate loop iteration - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const uptrT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - is_first_iter = false; - } - } while (is_cond); - mp.p_code = p_end - 1; - if (vsiz && is_first_iter) std::memset(&mp.mem[mem_proc] + 1,0,vsiz*sizeof(double)); - return is_first_iter?0:mp.mem[mem_proc]; - } - - static double mp_Ioff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const CImg &img = mp.imgin; - const long - off = (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const T *ptrs; - if (off<0 || off>=whd) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (!img) { - ptrs = &img[cimg::mod(off,whd)]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - case 1 : // Neumann boundary - if (img) { - ptrs = off<0?img._data:&img.back(); - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - default : // Dirichet boundary - std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - } - ptrs = &img[off]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type::nan(); - } - - static double mp_Ixyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - interpolation = (unsigned int)_mp_arg(5), - boundary_conditions = (unsigned int)_mp_arg(6); - const CImg &img = mp.imgin; - const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()),c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); - } - return cimg::type::nan(); - } - - static double mp_Joff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const CImg &img = mp.imgin; - const int ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], oz = (int)mp.mem[_cimg_mp_z]; - const long - off = img.offset(ox,oy,oz) + (long)_mp_arg(2), - whd = img.width()*img.height()*img.depth(); - const T *ptrs; - if (off<0 || off>=whd) - switch (boundary_conditions) { - case 2 : // Periodic boundary - if (!img) { - ptrs = &img[cimg::mod(off,whd)]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - case 1 : // Neumann boundary - if (img) { - ptrs = off<0?img._data:&img.back(); - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - default : // Dirichet boundary - std::memset(ptrd,0,img._spectrum*sizeof(double)); - return cimg::type::nan(); - } - ptrs = &img[off]; - cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type::nan(); - } - - static double mp_Jxyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - interpolation = (unsigned int)_mp_arg(5), - boundary_conditions = (unsigned int)_mp_arg(6); - const CImg &img = mp.imgin; - const double - ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z], - x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); - if (interpolation==0) { // Nearest neighbor interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); - } else { // Linear interpolation - if (boundary_conditions==2) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()),c); - else if (boundary_conditions==1) - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); - else - cimg_forC(img,c) - *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); - } - return cimg::type::nan(); - } - -#undef _mp_arg - - }; // struct _cimg_math_parser {} - - //! 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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=4096) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=262144) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=4096) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=4096) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=524288) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=2048) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=2048) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=2048) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=2048) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=2048) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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() { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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 (is_empty()) return *this; - if (p==-4) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); } - return *this; - } - if (p==-3) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); } - return *this; - } - if (p==-2) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); } - return *this; - } - if (p==-1) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); } - return *this; - } - if (p==-0.5) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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) return sqrt(); - if (p==1) return *this; - if (p==2) return sqr(); - if (p==3) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=262144) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } - return *this; - } - if (p==4) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=131072) -#endif - cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } - return *this; - } -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=1024) -#endif - cimg_rof(*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) { - return pow((+*this)._fill(expression,true,true,0,0,"pow",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return rol((+*this)._fill(expression,true,true,0,0,"rol",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) { - return ror((+*this)._fill(expression,true,true,0,0,"ror",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*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) { - return min((+*this)._fill(expression,true,true,0,0,"min",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) { - if (is_empty()) return *this; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*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) { - return max((+*this)._fill(expression,true,true,0,0,"max",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 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 product of all the pixel values. - /** - **/ - double product() const { - if (is_empty()) return 0; - double res = 1; - cimg_for(*this,ptrs,T) res*=(double)*ptrs; - return res; - } - - //! Return the sum of all the pixel values. - /** - **/ - double sum() const { - double res = 0; - cimg_for(*this,ptrs,T) res+=(double)*ptrs; - return res; - } - - //! Return the average pixel value. - /** - **/ - double mean() const { - double res = 0; - cimg_for(*this,ptrs,T) res+=(double)*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. - **/ - double variance(const unsigned int variance_method=1) const { - double 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 - double variance_mean(const unsigned int variance_method, t& mean) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "variance_mean(): Empty instance.", - cimg_instance); - - double variance = 0, average = 0; - const unsigned long siz = size(); - switch (variance_method) { - case 0 : { // Least mean square (standard definition) - double S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } - variance = (S2 - S*S/siz)/siz; - average = S; - } break; - case 1 : { // Least mean square (robust definition) - double S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const double val = (double)*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 double med_i = (double)buf[siz2]; - cimg_for(buf,ptrs,Tfloat) { - const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; - } - buf.sort(); - const double sig = (double)(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 double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; - } - buf.sort(); - double 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. - **/ - double 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 double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - CImg_3x3(I,T); - cimg_for3x3(*this,x,y,0,c,I,T) { - tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn + - (double)Icp - 4*(double)Icc); - } - } - } else { - const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=262144 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - CImg_3x3x3(I,T); - cimg_for3x3x3(*this,x,y,z,c,I,T) { - tmp(x,y,z,c) = cste*( - (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc + - (double)Iccn + (double)Iccp - 6*(double)Iccc); - } - } - } - return tmp.variance(variance_method); - } - - // Version that doesn't need intermediate images. - double variance = 0, S = 0, S2 = 0; - if (_depth==1) { - const double 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 double val = cste*((double)Inc + (double)Ipc + - (double)Icn + (double)Icp - 4*(double)Icc); - S+=val; S2+=val*val; - } - } else { - const double 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 double val = cste * - ((double)Incc + (double)Ipcc + (double)Icnc + - (double)Icpc + - (double)Iccn + (double)Iccp - 6*(double)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 - double 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); - double vMSE = 0; - const t* ptr2 = img._data; - cimg_for(*this,ptr1,T) { - const double diff = (double)*ptr1 - (double)*(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 - double PSNR(const CImg& img, const double max_value=255) const { - const double vMSE = (double)std::sqrt(MSE(img)); - return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(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. - \param list_inputs A list of input images attached to the specified math formula. - \param list_outputs A pointer to a list of output images attached to the specified math formula. - **/ - double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); - } - - //! Evaluate math formula \const. - double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); - } - - double _eval(CImg *const img_output, const char *const expression, - const double x, const double y, const double z, const double c, - const CImgList *const list_inputs, CImgList *const list_outputs) const { - if (!expression) return 0; - if (!expression[1]) switch (*expression) { // Single-char optimization. - case 'w' : return (double)_width; - case 'h' : return (double)_height; - case 'd' : return (double)_depth; - case 's' : return (double)_spectrum; - case 'r' : return (double)_is_shared; - } - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'?1:0),"eval", - *this,img_output,list_inputs,list_outputs); - return mp(x,y,z,c); - } - - //! Evaluate math formula. - /** - \param[out] output Contains values of output vector returned by the evaluated expression - (or is empty if the returned type is scalar). - \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. - \param list_inputs A list of input images attached to the specified math formula. - \param list_outputs A pointer to a list of output images attached to the specified math formula. - **/ - template - void eval(CImg &output, const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); - } - - //! Evaluate math formula \const. - template - void eval(CImg& output, const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); - } - - template - void _eval(CImg& output, CImg *const img_output, const char *const expression, - const double x, const double y, const double z, const double c, - const CImgList *const list_inputs, CImgList *const list_outputs) const { - if (!expression) { output.assign(1); *output = 0; } - if (!expression[1]) switch (*expression) { // Single-char optimization. - case 'w' : output.assign(1); *output = (t)_width; - case 'h' : output.assign(1); *output = (t)_height; - case 'd' : output.assign(1); *output = (t)_depth; - case 's' : output.assign(1); *output = (t)_spectrum; - case 'r' : output.assign(1); *output = (t)_is_shared; - } - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'?1:0),"eval", - *this,img_output,list_inputs,list_outputs); - output.assign(1,cimg::max(1U,mp.result_dim)); - mp(x,y,z,c,output._data); - } - - //! 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 CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _eval(this,expression,xyzc,list_inputs,list_outputs); - } - - //! Evaluate math formula on a set of variables \const. - template - CImg eval(const char *const expression, const CImg& xyzc, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - return _eval(0,expression,xyzc,list_inputs,list_outputs); - } - - template - CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - CImg res(1,xyzc.size()/4); - if (!expression) return res.fill(0); - _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs); -#ifdef cimg_use_openmp -#pragma omp parallel if (res._height>=512 && std::strlen(expression)>=6) - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for - for (unsigned int i = 0; i[min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax; sum; product]. - **/ - 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; - double S = 0, S2 = 0, P = _data?1:0; - T m = *pm, M = m; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - const double _val = (double)val; - if (valM) { M = val; pM = ptrs; } - S+=_val; - S2+=_val*_val; - P*=_val; - } - const double - 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,14).fill((double)m,(double)M,mean_value,variance_value, - (double)xm,(double)ym,(double)zm,(double)cm, - (double)xM,(double)yM,(double)zM,(double)cM, - S,P); - } - - //! 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 - **/ - double magnitude(const int magnitude_type=2) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "magnitude(): Empty instance.", - cimg_instance); - double res = 0; - switch (magnitude_type) { - case -1 : { - cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } - } break; - case 1 : { - cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs); - } break; - default : { - cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs); - res = (double)std::sqrt(res); - } - } - return res; - } - - //! Compute the trace of the image, viewed as a matrix. - /** - **/ - double trace() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "trace(): Empty instance.", - cimg_instance); - double res = 0; - cimg_forX(*this,k) res+=(double)(*this)(k,k); - return res; - } - - //! Compute the determinant of the image, viewed as a matrix. - /** - **/ - double 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 (double)((*this)(0,0)); - case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0)); - case 3 : { - const double - a = (double)_data[0], d = (double)_data[1], g = (double)_data[2], - b = (double)_data[3], e = (double)_data[4], h = (double)_data[5], - c = (double)_data[6], f = (double)_data[7], i = (double)_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); - double res = d?(double)1:(double)-1; - cimg_forX(lu,i) res*=lu(i,i); - return res; - } - } - } - - //! Compute the dot product between instance and argument, viewed as matrices. - /** - \param img Image used as a second argument of the dot product. - **/ - template - double 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()); - double 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 double delta = (double)a1 - (double)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 (_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) { // Classical linear system - if (_width!=1) { - CImg res(_width,A._width); - cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A)); - return res.move_to(*this); - } -#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 - if (_width!=1) { - CImg res(_width,A._width); - cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A)); - return res.move_to(*this); - } - 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) - 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 = (int)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 = (unsigned 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 eigenvectors, 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 eigenvectors, 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); - Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1.0f,cimg::abs(m),cimg::abs(M)); - (CImg(*this,false)/=maxabs).SVD(vec,val,V,false); - if (maxabs!=1) val*=maxabs; - - 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))/cimg::max((t)1e-25,2*h*y); - g = (t)cimg::_pythagore(f,1.0); - f = ((x - z)*(x + z)+h*((y/(f + (f>=0?g:-g))) - h))/cimg::max((t)1e-25,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/cimg::max((t)1e-25,z); s = h/cimg::max((t)1e-25,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/cimg::max((t)1e-25,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) = (unsigned int)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, const bool is_shared=false) { - if (!str) return CImg(); - return CImg(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); - } - - //! 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_values In case a list of values is provided, tells if this list must be repeated for the filling. - \param allow_formula tells if a formula is allowed or only a list of values. - \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. - \param list_outputs In case of a mathematical expression, attach a list of images to the specified expression. - **/ - CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0); - } - - CImg& _fill(const char *const expression, const bool repeat_values, const bool allow_formula, - const CImgList *const list_inputs, CImgList *const list_outputs, - const char *const calling_function, const CImg *provides_copy) { - if (is_empty() || !expression || !*expression) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - CImg is_error; - - if (allow_formula) try { // Try to fill values according to a formula - CImg base = provides_copy?provides_copy->get_shared():get_shared(); - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'?1:0), - calling_function,base,this,list_inputs,list_outputs); - if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && - mp.need_input_copy) - base.assign().assign(*this); // Needs input copy - - bool do_in_parallel = false; -#ifdef cimg_use_openmp - cimg_openmp_if(*expression=='*' || *expression==':' || - (mp.is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && - std::strlen(expression)>=6)) - do_in_parallel = true; -#endif - - if (mp.result_dim) { // Vector-valued expression - const unsigned int N = cimg::min(mp.result_dim,_spectrum); - const unsigned long whd = _width*_height*_depth; - T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; - if (*expression=='<') { - CImg res(1,mp.result_dim); - cimg_rofXYZ(*this,x,y,z) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } else if (*expression=='>' || !do_in_parallel) { - CImg res(1,mp.result_dim); - cimg_forXYZ(*this,x,y,z) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } else { -#ifdef cimg_use_openmp -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(2) - cimg_forYZ(*this,y,z) { - CImg res(1,lmp.result_dim); - T *ptrd = data(0,y,z,0); - cimg_forX(*this,x) { - lmp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - } -#endif - } - - } else { // Scalar-valued expression - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c); - else if (*expression=='>' || !do_in_parallel) - cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); - else { -#ifdef cimg_use_openmp -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); - } - } -#endif - } - } - } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } - - // If failed, try to recognize a list of values. - if (!allow_formula || is_error) { - char *const item = new char[16384], sep = 0; - const char *nexpression = expression; - unsigned long nb = 0; - const unsigned long siz = size(); - T *ptrd = _data; - for (double val = 0; *nexpression && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { - nexpression+=std::strlen(item) + (err>1?1:0); - *(ptrd++) = (T)val; - } else break; - } - delete[] item; - cimg::exception_mode(omode); - if (nb get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs); - } - - //! 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 sequence of values in the image buffer, along a specific axis. - /** - \param values Sequence of values to discard. - \param axis Axis along which the values are discarded. If set to \c 0 (default value) - the method does it for all the buffer values and returns a one-column vector. - \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, const char axis=0) { - if (is_empty() || !values) return *this; - return get_discard(values,axis).move_to(*this); - } - - template - CImg get_discard(const CImg& values, const char axis=0) const { - CImg res; - if (!values) return +*this; - if (is_empty()) return res; - const unsigned long vsiz = values.size(); - const char _axis = cimg::uncase(axis); - unsigned long j = 0; - unsigned int k = 0; - int i0 = 0; - res.assign(width(),height(),depth(),spectrum()); - switch (_axis) { - case 'x' : { - cimg_forX(*this,i) { - if ((*this)(i)!=(T)values[j]) { - if (j) --i; - res.draw_image(k,get_columns(i0,i)); - k+=i - i0 + 1; i0 = i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0=vsiz) { j = 0; i0 = (int)i + 1; }} - } - const unsigned long siz = size(); - if ((unsigned long)i0& discard(const char axis=0) { - return get_discard(axis).move_to(*this); - } - - //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance. - CImg get_discard(const char axis=0) const { - CImg res; - if (is_empty()) return res; - const char _axis = cimg::uncase(axis); - T current = *_data?0:(T)1; - int j = 0; - res.assign(width(),height(),depth(),spectrum()); - switch (_axis) { - case 'x' : { - cimg_forX(*this,i) - if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); } - res.resize(j,-100,-100,-100,0); - } break; - case 'y' : { - cimg_forY(*this,i) - if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); } - res.resize(-100,j,-100,-100,0); - } break; - case 'z' : { - cimg_forZ(*this,i) - if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); } - res.resize(-100,-100,j,-100,0); - } break; - case 'c' : { - cimg_forC(*this,i) - if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); } - res.resize(-100,-100,-100,j,0); - } break; - default : { - res.unroll('y'); - cimg_foroff(*this,i) - if ((*this)[i]!=current) res[j++] = current = (*this)[i]; - res.resize(-100,j,-100,-100,0); - } - } - return res; - } - - //! 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 authorized random value. - \param val_max Maximal authorized random value. - \note Random variables are uniformely distributed in [val_min,val_max]. - **/ - CImg& rand(const T& val_min, const T& val_max) { - const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); - if (cimg::type::is_float()) cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta); - else cimg_for(*this,ptrd,T) *ptrd = cimg::min(val_max,(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) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=8192) -#endif - cimg_rof(*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()) return *this; - 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_rof(*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_rof(*this,ptrd,T) if (cimg::rand(100)vmax) 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=65536) -#endif - cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a); - return *this; - } - - //! Linearly normalize pixel values \newinstance. - CImg 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() { - const unsigned long whd = (unsigned long)_width*_height*_depth; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - T *ptrd = data(0,y,z,0); - cimg_forX(*this,x) { - 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 Lp-norm of each multi-valued pixel of the image instance. - /** - \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p>=0). - \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 && norm_type) 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 && norm_type) return get_abs(); - const unsigned long whd = (unsigned long)_width*_height*_depth; - CImg res(_width,_height,_depth); - switch (norm_type) { - case -1 : { // Linf-norm. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - const unsigned long off = (unsigned long)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - 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 0 : { // L0-norm. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - const unsigned long off = (unsigned long)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - unsigned int n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } - *(ptrd++) = (Tfloat)n; - } - } - } break; - case 1 : { // L1-norm. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - const unsigned long off = (unsigned long)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } - *(ptrd++) = n; - } - } - } break; - case 2 : { // L2-norm. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - const unsigned long off = (unsigned long)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } - *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); - } - } - } break; - default : { // Linf-norm. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) -#endif - cimg_forYZ(*this,y,z) { - const unsigned long off = (unsigned long)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; } - *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type); - } - } - } - } - 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_value=32768) -#endif - cimg_rof(*this,ptrd,T) *ptrd = (*ptrdb)?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) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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 -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*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) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { - const T v = *ptrd; - *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; - } - else -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; - } else { - if (soft_threshold) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=32768) -#endif - cimg_rof(*this,ptrd,T) { - const T v = *ptrd; - *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; - } - else -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=65536) -#endif - cimg_rof(*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. - - 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, const T& max_value) { - return get_histogram(nb_levels,min_value,max_value).move_to(*this); - } - - //! Compute the histogram of pixel values \overloading. - CImg& histogram(const unsigned int nb_levels) { - return get_histogram(nb_levels).move_to(*this); - } - - //! Compute the histogram of pixel values \newinstance. - CImg get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { - if (!nb_levels || is_empty()) return CImg(); - const double - vmin = (double)(min_value res(nb_levels,1,1,1,0); - cimg_rof(*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; - } - - //! Compute the histogram of pixel values \newinstance. - CImg get_histogram(const unsigned int nb_levels) const { - if (!nb_levels || is_empty()) return CImg(); - T vmax = 0, vmin = min_max(vmax); - return get_histogram(nb_levels,vmin,vmax); - } - - //! 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. - \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, const T& max_value) { - if (!nb_levels || is_empty()) return *this; - const T - vmin = min_value hist = get_histogram(nb_levels,vmin,vmax); - unsigned long cumul = 0; - cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } - if (!cumul) cumul = 1; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(size()>=1048576) -#endif - cimg_rof(*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]/cumul); - } - return *this; - } - - //! Equalize histogram of pixel values \overloading. - CImg& equalize(const unsigned int nb_levels) { - if (!nb_levels || is_empty()) return *this; - T vmax = 0, vmin = min_max(vmax); - return equalize(nb_levels,vmin,vmax); - } - - //! Equalize histogram of pixel values \newinstance. - CImg get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { - return (+*this).equalize(nblevels,val_min,val_max); - } - - //! Equalize histogram of pixel values \newinstance. - CImg get_equalize(const unsigned int nblevels) const { - return (+*this).equalize(nblevels); - } - - //! 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=64 && _height*_depth>=16 && pwhd>=16) -#endif - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z); - for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0=64 && _height*_depth>=16 && pwhd>=16) -#endif - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd; - for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0=64 && _height*_depth>=16 && pwhd>=16) -#endif - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; - for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, - *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, - *ptrp_end = ptrp1; ptrp0=64 && _height*_depth>=16 && pwhd>=16) -#endif - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z); - for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs::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, const unsigned int boundary_conditions=0) { - return get_map(colormap,boundary_conditions).move_to(*this); - } - - //! Map predefined colormap on the scalar (indexed) image instance \newinstance. - template - CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) 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; - switch (boundary_conditions) { - case 2 : // Periodic boundaries. - cimg_for(res,ptrd,t) { - const unsigned long ind = (unsigned long)*(ptrs++); - *ptrd = colormap[ind%pwhd]; - } break; - case 1 : // Neumann boundaries. - cimg_for(res,ptrd,t) { - const long ind = (long)*(ptrs++); - *ptrd = colormap[ind<0?0:ind>=(long)pwhd?pwhd - 1:ind]; - } break; - default : // Dirichlet boundaries. - cimg_for(res,ptrd,t) { - const unsigned long ind = (unsigned long)*(ptrs++); - *ptrd = ind=(long)pwhd?(long)pwhd - 1:_ind; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; - } - } break; - default : { // Dirichlet boundaries. - const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd; - t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd; - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs=(long)pwhd?(long)pwhd - 1:_ind; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind]; - } - } break; - default : { // Dirichlet boundaries. - const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd; - t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd; - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs=(long)pwhd?(long)pwhd - 1:_ind; - const t *ptrp = colormap._data + ind; - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; } - } - } break; - default : { // Dirichlet boundaries. - t *ptrd = res._data; - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs& 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 = cimg::mod((Tfloat)*p1,(Tfloat)360), - 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 = cimg::mod((Tfloat)*p1,(Tfloat)360), - 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 = cimg::mod((Tfloat)*p1,(Tfloat)360), - 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)), - cX = a/500 + cY, - X = (Tfloat)(Xn*_cimg_Labfi(cX)), - cZ = cY - b/200, - Z = (Tfloat)(Zn*_cimg_Labfi(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(); - } - - //@} - //------------------------------------------ - // - //! \name Geometric / Spatial Manipulation - //@{ - //------------------------------------------ - - static float _cimg_lanczos(const float x) { - if (x<=-2 || 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 : { // Periodic boundary. - 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(); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=65536) -#endif - 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 boundary. - 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 boundary. - 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); - 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 { - unsigned long *poff_x = off_x._data, curr = 0; - cimg_forX(res,x) { - const unsigned long old = curr; - curr = (unsigned long)((x + 1.0)*_width/sx); - *(poff_x++) = curr - old; - } - } - if (sy==_height) off_y.fill(_width); - else { - unsigned long *poff_y = off_y._data, curr = 0; - cimg_forY(res,y) { - const unsigned long old = curr; - curr = (unsigned long)((y + 1.0)*_height/sy); - *(poff_y++) = _width*(curr - old); - } - *poff_y = 0; - } - if (sz==_depth) off_z.fill(wh); - else { - unsigned long *poff_z = off_z._data, curr = 0; - cimg_forZ(res,z) { - const unsigned long old = curr; - curr = (unsigned long)((z + 1.0)*_depth/sz); - *(poff_z++) = wh*(curr - old); - } - *poff_z = 0; - } - if (sc==_spectrum) off_c.fill(whd); - else { - unsigned long *poff_c = off_c._data, curr = 0; - cimg_forC(res,c) { - const unsigned long old = curr; - curr = (unsigned long)((c + 1.0)*_spectrum/sc); - *(poff_c++) = whd*(curr - old); - } - *poff_c = 0; - } - - T *ptrd = res._data; - const T* ptrc = _data; - const unsigned long *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); - CImg resx, resy, resz, resc; - - 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fx; - *(poff++) = (unsigned int)curr - (unsigned int)old; - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resx.size()>=65536) -#endif - cimg_forYZC(resx,y,z,c) { - const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const float *pfoff = foff._data; - 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fy; - *(poff++) = sx*((unsigned int)curr-(unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resy.size()>=65536) -#endif - cimg_forXZC(resy,x,z,c) { - const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fz; - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resz.size()>=65536) -#endif - cimg_forXYC(resz,x,y,c) { - const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fc; - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resc.size()>=65536) -#endif - cimg_forXYZ(resc,x,y,z) { - const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const float *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 = (int)(2*sx), dy = 2*width(); - 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 = (int)(2*sy), dy = 2*height(); - 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 = (int)(2*sz), dy = 2*depth(); - 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 = (int)(2*sc), dy = 2*spectrum(); - 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); - CImg resx, resy, resz, resc; - - 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fx; - *(poff++) = (unsigned int)curr - (unsigned int)old; - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resx.size()>=65536) -#endif - cimg_forYZC(resx,y,z,c) { - const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const float *pfoff = foff._data; - 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++); - } - } - } - } - } 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fy; - *(poff++) = sx*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resy.size()>=65536) -#endif - cimg_forXZC(resy,x,z,c) { - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fz; - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resz.size()>=65536) -#endif - cimg_forXYC(resz,x,y,c) { - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fc; - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resc.size()>=65536) -#endif - cimg_forXYZ(resc,x,y,z) { - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const float *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); - CImg resx, resy, resz, resc; - - 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fx; - *(poff++) = (unsigned int)curr - (unsigned int)old; - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resx.size()>=65536) -#endif - cimg_forYZC(resx,y,z,c) { - const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, - *const ptrsmax = ptrs0 + (_width - 2); - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const float *pfoff = foff._data; - 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++); - } - } - } - } - } 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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fy; - *(poff++) = sx*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resy.size()>=65536) -#endif - cimg_forXZC(resy,x,z,c) { - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, - *const ptrsmax = ptrs0 + (_height - 2)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fz; - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resz.size()>=65536) -#endif - cimg_forXYC(resz,x,y,c) { - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, - *const ptrsmax = ptrs0 + (_depth - 2)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const float *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); - float curr = 0, old = 0; - unsigned int *poff = off._data; - float *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr+=fc; - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (resc.size()>=65536) -#endif - cimg_forXYZ(resc,x,y,z) { - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, - *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const float *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; - static const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, - 0.1231940459f, 0.1935127547f, 0.1231940459f, - 0.07842776544f, 0.1231940459f, 0.07842776544f }; - CImg I(9), 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 characters, 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 (cimg::uncase(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=periodic }. - \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) { - const float nangle = cimg::mod(angle,360.0f); - if (nangle==0.0f) return *this; - 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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { // Periodic boundaries. - switch (interpolation) { - case 2 : { // Cubic interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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=periodic }).", - 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=periodic }. - \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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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 : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=2048) -#endif - cimg_forXYZC(res,x,y,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=periodic }).", - cimg_instance, - boundary); - } - return res; - } - - //! Warp image content by a warping field. - /** - \param warp Warping field. - \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative } - \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=periodic }. - **/ - template - CImg& warp(const CImg& warp, const unsigned int mode=0, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { - return get_warp(warp,mode,interpolation,boundary_conditions).move_to(*this); - } - - //! Warp image content by a warping field \newinstance - template - CImg get_warp(const CImg& warp, const unsigned int mode=0, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { - if (is_empty() || !warp) return *this; - if (mode && !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); - - if (warp._spectrum==1) { // 1d warping. - if (mode>=3) { // Forward-relative warp. - res.fill(0); - if (interpolation>=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = x + (int)*(ptrs0++); - if (X>=0 && X=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = (int)*(ptrs0++); - if (X>=0 && X=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); - } - else if (boundary_conditions==1) // Neumann boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(x - (float)*(ptrs0++),y,z,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atX(x - (float)*(ptrs0++),y,z,c,0); - } - } else if (interpolation==1) { // Linear interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); - } - else if (boundary_conditions==1) // Neumann boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0); - } - } else { // Nearest-neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c); - } - else if (boundary_conditions==1) // Neumann boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0); - } - } - } else { // Backward-absolute warp. - if (interpolation==2) { // Cubic interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); - } - else if (boundary_conditions==1) // Neumann boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX((float)*(ptrs0++),0,0,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atX((float)*(ptrs0++),0,0,c,0); - } - } else if (interpolation==1) { // Linear interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); - } - else if (boundary_conditions==1) // Neumann boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0); - } - } else { // Nearest-neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c); - } - else if (boundary_conditions==1) // Neumann boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0); - } - } - } - - } else if (warp._spectrum==2) { // 2d warping. - if (mode>=3) { // Forward-relative warp. - res.fill(0); - if (interpolation>=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = x + (int)*(ptrs0++), Y = y + (int)*(ptrs1++); - if (X>=0 && X=0 && Y=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = (int)*(ptrs0++), Y = (int)*(ptrs1++); - if (X>=0 && X=0 && Y=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0); - } - } else if (interpolation==1) { // Linear interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0); - } - } else { // Nearest-neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0); - } - } - } else { // Backward-absolute warp. - if (interpolation==2) { // Cubic interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0); - } - } else if (interpolation==1) { // Linear interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0); - } - } else { // Nearest-neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width), - cimg::mod((int)*(ptrs1++),(int)_height),0,c); - } - else if (boundary_conditions==1) // Neumann boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0); - } - } - } - - } else { // 3d warping. - if (mode>=3) { // Forward-relative warp. - res.fill(0); - if (interpolation>=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++), - z + (float)*(ptrs2++),c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = x + (int)*(ptrs0++), Y = y + (int)*(ptrs1++), Z = z + (int)*(ptrs2++); - if (X>=0 && X=0 && Y=0 && Z=1) // Linear interpolation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - else // Nearest-neighbor interpolation. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = (int)*(ptrs0++), Y = (int)*(ptrs1++), Z = (int)*(ptrs2++); - if (X>=0 && X=0 && Y=0 && Z=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = (T)_cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(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) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0); - } - } else { // Nearest neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0); - } - } - } else { // Backward-absolute warp. - if (interpolation==2) { // Cubic interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=4096) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0); - } - } else if (interpolation==1) { // Linear interpolation. - if (boundary_conditions==2) // Periodic boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - else // Dirichlet boundaries. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (res.size()>=1048576) -#endif - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0); - } - } else { // Nearest-neighbor interpolation. - if (boundary_conditions==2) // Periodic boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c); - } - else // Dirichlet boundaries. - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(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(expr,"streamline",CImg::const_empty(),0); - } - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return (float)(*mp)(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 = (unsigned int)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 = (unsigned int)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 = (unsigned int)offset(0,y0,z0,c0), - end = (unsigned int)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 = (unsigned int)offset(0,y0,z0,c0), - end = (unsigned int)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 = (unsigned int)offset(0,0,z0,c0), - end = (unsigned int)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 = (unsigned int)offset(0,0,z0,c0), - end = (unsigned int)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 = (unsigned int)offset(0,0,0,c0), - end = (unsigned int)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 = (unsigned int)offset(0,0,0,c0), - end = (unsigned int)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, instance image is splitted into blocs of egal values 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=-1) 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); - switch (_axis) { - case 'x': { - if (_width>dp) { - res.assign(_width/dp + (_width%dp?1:0),1,1); - const unsigned int pe = _width - dp; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=128 && _height*_depth*_spectrum>=128) -#endif - for (unsigned int p = 0; pdp) { - res.assign(_height/dp + (_height%dp?1:0),1,1); - const unsigned int pe = _height - dp; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_depth*_spectrum>=128) -#endif - for (unsigned int p = 0; pdp) { - res.assign(_depth/dp + (_depth%dp?1:0),1,1); - const unsigned int pe = _depth - dp; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_height*_spectrum>=128) -#endif - for (unsigned int p = 0; pdp) { - res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1); - const unsigned int pe = _spectrum - dp; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_height*_depth>=128) -#endif - for (unsigned int p = 0; p0) { // Split by number of (non-homogeneous) blocs. - const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0; - if ((unsigned int)nb>siz) - throw CImgArgumentException(_cimg_instance - "get_split(): Instance cannot be split along %c-axis into %u blocs.", - cimg_instance, - axis,nb); - if (nb==1) res.assign(*this); - else { - 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 + 1U; - } - } 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 + 1U; - } - } 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 + 1U; - } - } break; - case 'c' : { - 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 + 1U; - } - } - } - } - } else { // Split by egal values according to specified axis. - T current = *_data; - switch (_axis) { - case 'x' : { - int i0 = 0; - cimg_forX(*this,i) - if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); } - get_columns(i0,width() - 1).move_to(res); - } break; - case 'y' : { - int i0 = 0; - cimg_forY(*this,i) - if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); } - get_rows(i0,height() - 1).move_to(res); - } break; - case 'z' : { - int i0 = 0; - cimg_forZ(*this,i) - if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); } - get_slices(i0,depth() - 1).move_to(res); - } break; - case 'c' : { - int i0 = 0; - cimg_forC(*this,i) - if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); } - get_channels(i0,spectrum() - 1).move_to(res); - } break; - default : { - long i0 = 0; - cimg_foroff(*this,i) - if ((*this)[i]!=current) { CImg(_data + i0,1,i - i0).move_to(res); i0 = (long)i; current = (*this)[i]; } - CImg(_data + i0,1,size() - i0).move_to(res); - } - } - } - return res; - } - - //! Split image into a list of sub-images, according to a specified splitting value sequence and optionnally axis. - /** - \param values Splitting value sequence. - \param axis Axis along which the splitting is performed. Can be '0' to ignore axis. - \param keep_values Tells if the splitting sequence must be kept in the splitted blocs. - **/ - template - CImgList get_split(const CImg& values, const char axis=0, const bool keep_values=true) const { - CImgList res; - if (is_empty()) return res; - const unsigned long vsiz = values.size(); - const char _axis = cimg::uncase(axis); - if (!vsiz) return CImgList(*this); - if (vsiz==1) { // Split according to a single value. - const T value = *values; - switch (_axis) { - case 'x' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_width && (*this)(i)==value) ++i; - if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; } - while (i<_width && (*this)(i)!=value) ++i; - if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; } - } while (i<_width); - } break; - case 'y' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_height && (*this)(0,i)==value) ++i; - if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; } - while (i<_height && (*this)(0,i)!=value) ++i; - if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; } - } while (i<_height); - } break; - case 'z' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_depth && (*this)(0,0,i)==value) ++i; - if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; } - while (i<_depth && (*this)(0,0,i)!=value) ++i; - if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; } - } while (i<_depth); - } break; - case 'c' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_spectrum && (*this)(0,0,0,i)==value) ++i; - if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; } - while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i; - if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; } - } while (i<_spectrum); - } break; - default : { - const unsigned long siz = size(); - unsigned long i0 = 0, i = 0; - do { - while (ii0) { if (keep_values) CImg(_data + i0,1,i - i0).move_to(res); i0 = i; } - while (ii0) { CImg(_data + i0,1,i - i0).move_to(res); i0 = i; } - } while (i=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_columns(i0,i1 - 1).move_to(res); - if (keep_values) get_columns(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_width); - if (i0<_width) get_columns(i0,width() - 1).move_to(res); - } break; - case 'y' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,i)==*values) { - i1 = i; j = 0; - while (i<_height && (*this)(0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_rows(i0,i1 - 1).move_to(res); - if (keep_values) get_rows(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_height); - if (i0<_height) get_rows(i0,height() - 1).move_to(res); - } break; - case 'z' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,0,i)==*values) { - i1 = i; j = 0; - while (i<_depth && (*this)(0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_slices(i0,i1 - 1).move_to(res); - if (keep_values) get_slices(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_depth); - if (i0<_depth) get_slices(i0,depth() - 1).move_to(res); - } break; - case 'c' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,0,0,i)==*values) { - i1 = i; j = 0; - while (i<_spectrum && (*this)(0,0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_channels(i0,i1 - 1).move_to(res); - if (keep_values) get_channels(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_spectrum); - if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res); - } break; - default : { - unsigned long i0 = 0, i1 = 0, i = 0; - const unsigned long siz = size(); - do { - if ((*this)[i]==*values) { - i1 = i; j = 0; - while (i=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) CImg(_data + i0,1,i1 - i0).move_to(res); - if (keep_values) CImg(_data + i1,1,i - i1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i(_data + i0,1,siz - i0).move_to(res); - } break; - } - } - 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; - CImg I; - switch (mask._depth) { - case 3 : { - I.assign(27); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(8); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(36); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(25); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(16); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(9); - cimg_forC(res,c) { - cimg_test_abort(); - 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 : { - I.assign(4); - cimg_forC(res,c) { - cimg_test_abort(); - 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) { - cimg_test_abort(); - 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 boundary 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._spectrum>=2) -#endif - cimg_forC(res,c) { - cimg_test_abort(); - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768) -#endif - for (int z = mz1; z=256 && _height*_depth>=128) -#endif - 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); - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768) -#endif - for (int z = mz1; z=256 && _height*_depth>=128) -#endif - 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)*_mask(mx1 + xm,my1 + ym,mz1 + zm); - res(x,y,z,c) = (Ttfloat)val; - } - else -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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); - } - - //! Cumulate image values, optionally along specified axis. - /** - \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account. - **/ - CImg& cumulate(const char axis=0) { - switch (cimg::uncase(axis)) { - case 'x' : -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=512 && _height*_depth*_spectrum>=16) -#endif - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - Tlong cumul = 0; - cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; } - } - break; - case 'y' : { - const unsigned long w = (unsigned long)_width; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_height>=512 && _width*_depth*_spectrum>=16) -#endif - cimg_forXZC(*this,x,z,c) { - T *ptrd = data(x,0,z,c); - Tlong cumul = 0; - cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; } - } - } break; - case 'z' : { - const unsigned long wh = (unsigned long)_width*_height; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_depth>=512 && _width*_depth*_spectrum>=16) -#endif - cimg_forXYC(*this,x,y,c) { - T *ptrd = data(x,y,0,c); - Tlong cumul = 0; - cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; } - } - } break; - case 'c' : { - const unsigned long whd = (unsigned long)_width*_height*_depth; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_spectrum>=512 && _width*_height*_depth>=16) -#endif - cimg_forXYZ(*this,x,y,z) { - T *ptrd = data(x,y,z,0); - Tlong cumul = 0; - cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; } - } - } break; - default : { // Global cumulation. - Tlong cumul = 0; - cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; } - } - } - return *this; - } - - //! Cumulate image values, optionally along specified axis \newinstance. - CImg get_cumulate(const char axis=0) const { - return CImg(*this,false).cumulate(axis); - } - - //! Cumulate image values, along specified axes. - /** - \param axes Cumulation axes, as a C-string. - \note \c axes may contains multiple characters, e.g. \c "xyz" - **/ - CImg& cumulate(const char *const axes) { - for (const char *s = axes; *s; ++s) cumulate(*s); - return *this; - } - - //! Cumulate image values, along specified axes \newintance. - CImg get_cumulate(const char *const axes) const { - return CImg(*this,false).cumulate(axes); - } - - //! 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - cimg_test_abort(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); - if (is_normalized) { // Normalized erosion. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768) -#endif - 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=256 && _height*_depth>=128) -#endif - cimg_forYZ(res,y,z) - for (int x = 0; x=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=256 && _height*_depth>=128) -#endif - cimg_forYZ(res,y,z) - for (int x = 0; x=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=32768) -#endif - for (int z = mz1; z::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Tt cval = (Tt)_img(x + xm,y + ym,z + zm); - if (_mask(mx1 + xm,my1 + ym,mz1 + zm) && cval=256 && _height*_depth>=128) -#endif - cimg_forYZ(res,y,z) - for (int x = 0; x=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=256 && _height*_depth>=128) -#endif - cimg_forYZ(res,y,z) - for (int x = 0; x=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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forYZC(*this,y,z,c) { - T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(0,y,z,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forXZC(*this,x,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(x,0,z,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forXYC(*this,x,y,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(x,y,0,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - cimg_test_abort(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); - if (is_normalized) { // Normalized dilation. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768) -#endif - 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) -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth>=128) -#endif - 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) -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128) -#endif - 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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forYZC(*this,y,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(0,y,z,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forXZC(*this,x,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(x,0,z,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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); -#ifdef cimg_use_opemp -#pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288) -#endif - cimg_forXYC(*this,x,y,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - 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; - if (ptrs>=ptrse) { - T *pd = data(x,y,0,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - 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 is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity - in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case. - \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 is_high_connectivity=false) { -#define _cimg_watershed_init(cond,X,Y,Z) \ - if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds) - -#define _cimg_watershed_propagate(cond,X,Y,Z) \ - if (cond) { \ - if ((*this)(X,Y,Z)) { \ - ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \ - d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \ - if (d labels(_width,_height,_depth,1,0), seeds(64,3); - CImg::type> Q; - unsigned int sizeQ = 0; - int px, nx, py, ny, pz, nz; - bool is_px, is_nx, is_py, is_ny, is_pz, is_nz; - const bool is_3d = _depth>1; - - // Find seed points and insert them in priority queue. - unsigned int nb_seeds = 0; - const T *ptrs = _data; - cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3d version - if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0); - seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z; - px = x - 1; nx = x + 1; - py = y - 1; ny = y + 1; - pz = z - 1; nz = z + 1; - is_px = px>=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz::inf(); - T label = 0; - _cimg_watershed_propagate(is_px,px,y,z); - _cimg_watershed_propagate(is_nx,nx,y,z); - _cimg_watershed_propagate(is_py,x,py,z); - _cimg_watershed_propagate(is_ny,x,ny,z); - if (is_3d) { - _cimg_watershed_propagate(is_pz,x,y,pz); - _cimg_watershed_propagate(is_nz,x,y,nz); - } - if (is_high_connectivity) { - _cimg_watershed_propagate(is_px && is_py,px,py,z); - _cimg_watershed_propagate(is_nx && is_py,nx,py,z); - _cimg_watershed_propagate(is_px && is_ny,px,ny,z); - _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z); - if (is_3d) { - _cimg_watershed_propagate(is_px && is_pz,px,y,pz); - _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz); - _cimg_watershed_propagate(is_px && is_nz,px,y,nz); - _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz); - _cimg_watershed_propagate(is_py && is_pz,x,py,pz); - _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz); - _cimg_watershed_propagate(is_py && is_nz,x,py,nz); - _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz); - _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz); - _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz); - _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz); - _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz); - _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz); - _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz); - _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz); - _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz); - } - } - (*this)(x,y,z) = label; - labels(x,y,z) = ++nmin; - } - return *this; - } - - //! Compute watershed transform \newinstance. - template - CImg get_watershed(const CImg& priority, const bool is_high_connectivity=false) const { - return (+*this).watershed(priority,is_high_connectivity); - } - - // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. - template - bool _priority_queue_insert(CImg& is_queued, unsigned int& siz, const tv value, - const unsigned int x, const unsigned int y, const unsigned int z, - const unsigned int n=1) { - if (is_queued(x,y,z)) return false; - is_queued(x,y,z) = (tq)n; - 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 unsigned int order=0, const char axis='x', - const bool boundary_conditions=true) { -#define _cimg_deriche_apply \ - CImg Y(N); \ - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - 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 unsigned 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 Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). - */ - static void _cimg_recursive_apply(T *data, const double filter[], const int N, const unsigned long off, - const unsigned int order, const bool boundary_conditions) { - double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] - const double - sumsq = filter[0], sum = sumsq * sumsq, - a1 = filter[1], a2 = filter[2], a3 = filter[3], - scaleM = 1.0 / ( (1.0 + a1 - a2 + a3) * (1.0 - a1 - a2 - a3) * (1.0 + a2 + (a1 - a3) * a3) ); - double M[9]; // Triggs matrix - M[0] = scaleM * (-a3 * a1 + 1.0 - a3 * a3 - a2); - M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1); - M[2] = scaleM * a3 * (a1 + a3 * a2); - M[3] = scaleM * (a1 + a3 * a2); - M[4] = -scaleM * (a2 - 1.0) * (a2 + a3 * a1); - M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.0); - M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2); - M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3); - M[8] = scaleM * a3 * (a1 + a3 * a2); - switch (order) { - case 0 : { - const double iplus = (boundary_conditions?data[(N - 1)*off]:0); - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0); - } else { - /* apply Triggs border condition */ - const double - uplus = iplus/(1.0 - a1 - a2 - a3), vplus = uplus/(1.0 - a1 - a2 - a3), - unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n0; --k) val[k] = val[k - 1]; - } - if (!pass) data -= off; - } - } break; - case 1 : { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n0; --k) x[k] = x[k - 1]; - } else { data-=off;} - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - *data = (T)0; - } - } break; - case 2: { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n0; --k) x[k] = x[k - 1]; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - *data = (T)0; - } - } break; - case 3: { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n0; --k) x[k] = x[k - 1]; - for (int k = 3; 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 - - I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. - IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002. - - (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995) - - Boundary conditions (only for order 0) using Triggs matrix, from - B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet - recursive filtering. IEEE Trans. Signal Processing, - vol. 54, pp. 2365-2367, 2006. - **/ - CImg& vanvliet(const float sigma, const unsigned 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.5f && !order)) return *this; - const double - nnsigma = nsigma<0.5f?0.5f:nsigma, - m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, - m1sq = m1 * m1, m2sq = m2 * m2, - q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), - qsq = q * q, - scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq), - b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale, - b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale, - b3 = -qsq * q / scale, - B = ( m0 * (m1sq + m2sq) ) / scale; - double filter[4]; - filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3; - switch (naxis) { - case 'x' : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forYZC(*this,y,z,c) - _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); - } break; - case 'y' : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXZC(*this,x,z,c) - _cimg_recursive_apply(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) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXYC(*this,x,y,c) - _cimg_recursive_apply(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) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXYZ(*this,x,y,z) - _cimg_recursive_apply(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 unsigned 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()) return *this; - 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_forC(*this,c) { - cimg_test_abort(); - CImg_3x3x3(I,Tfloat); - 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_forZC(*this,z,c) { - cimg_test_abort(); - CImg_3x3(I,Tfloat); - 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,1,1,1,0); - 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; - } - - cimg_test_abort(); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=2) firstprivate(val) -#endif - 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 = res.data(x,y,z); - 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; - } - - cimg_test_abort(); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val) -#endif - 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 = res.data(x,y); - 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 CImg(*this,false).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 CImg(*this,false).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 sampling_x Amount of downsampling along the X-axis used for the approximation. - Defaults (0) to sigma_x. - \param sampling_y Amount of downsampling along the Y-axis used for the approximation. - Defaults (0) to sigma_y. - \param sampling_z Amount of downsampling along the Z-axis used for the approximation. - Defaults (0) to sigma_z. - \param sampling_r Amount of downsampling along the value axis used for the approximation. - Defaults (0) to sigma_r. - \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 - (extended for 3d volumetric images). - It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m - **/ - template - CImg& blur_bilateral(const CImg& guide, - const float sigma_x, const float sigma_y, - const float sigma_z, const float sigma_r, - const float sampling_x, const float sampling_y, - const float sampling_z, const float sampling_r) { - 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 edge_min, edge_max = guide.max_min(edge_min); - if (edge_min==edge_max || sigma_r == 0.) return *this; - const float - edge_delta = (float)(edge_max - edge_min), - _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*(edge_max-edge_min)/100, - _sampling_x = sampling_x?sampling_x:cimg::max(_sigma_x,1.0f), - _sampling_y = sampling_y?sampling_y:cimg::max(_sigma_y,1.0f), - _sampling_z = sampling_z?sampling_z:cimg::max(_sigma_z,1.0f), - _sampling_r = sampling_r?sampling_r:cimg::max(_sigma_r,edge_delta/256), - derived_sigma_x = _sigma_x / _sampling_x, - derived_sigma_y = _sigma_y / _sampling_y, - derived_sigma_z = _sigma_z / _sampling_z, - derived_sigma_r = _sigma_r / _sampling_r; - const int - padding_x = (int)(2*derived_sigma_x) + 1, - padding_y = (int)(2*derived_sigma_y) + 1, - padding_z = (int)(2*derived_sigma_z) + 1, - padding_r = (int)(2*derived_sigma_r) + 1; - const unsigned int - bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x), - by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y), - bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z), - br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r); - if (bx>0 || by>0 || bz>0 || br>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 edge = (float)_guide(x,y,z); - const int - X = (int)cimg::round(x/_sampling_x) + padding_x, - Y = (int)cimg::round(y/_sampling_y) + padding_y, - Z = (int)cimg::round(z/_sampling_z) + padding_z, - R = (int)cimg::round((edge-edge_min)/_sampling_r) + padding_r; - bgrid(X,Y,Z,R)+=(float)val; - bgridw(X,Y,Z,R)+=1; - } - bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); - bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); - cimg_forXYZ(*this,x,y,z) { - const float edge = (float)_guide(x,y,z); - const float - X = x/_sampling_x + padding_x, - Y = y/_sampling_y + padding_y, - Z = z/_sampling_z + padding_z, - R = (edge-edge_min)/_sampling_r + padding_r; - const float 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 { // 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 edge = (float)_guide(x,y); - const int - X = (int)cimg::round(x/_sampling_x) + padding_x, - Y = (int)cimg::round(y/_sampling_y) + padding_y, - R = (int)cimg::round((edge-edge_min)/_sampling_r) + padding_r; - bgrid(X,Y,R,0)+=(float)val; - bgrid(X,Y,R,1)+=1; - } - bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false); - cimg_forXY(*this,x,y) { - const float edge = (float)_guide(x,y); - const float - X = x/_sampling_x + padding_x, - Y = y/_sampling_y + padding_y, - R = (edge-edge_min)/_sampling_r + padding_r; - const float bval0 = bgrid.linear_atXYZ(X,Y,R,0), bval1 = bgrid.linear_atXYZ(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 float sampling_x, const float sampling_y, - const float sampling_z, const float sampling_r) const { - return CImg(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, - sampling_x,sampling_y,sampling_z,sampling_r); - } - - //! 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 sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s. - \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r. - **/ - template - CImg& blur_bilateral(const CImg& guide, - const float sigma_s, const float sigma_r, - const float sampling_s=0, const float sampling_r=0) { - const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; - return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r); - } - - //! Blur image using the bilateral filter \newinstance. - template - CImg get_blur_bilateral(const CImg& guide, - const float sigma_s, const float sigma_r, - const float sampling_s=0, const float sampling_r=0) const { - return CImg(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); - } - - // [internal] Apply a box filter (used by CImg::boxfilter() and CImg::blur_box()). - /* - \param ptr the pointer of the data - \param N size of the data - \param sigma sigma of the box filter - \param off the offset between two data point - \param order the order of the filter 0 (smoothing), 1st derivtive and 2nd derivative. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. - */ - static void _cimg_blur_box_apply(T *ptr, const float sigma, const int N, const unsigned long off, - const int order, const bool boundary_conditions) { - // Smooth. - if (sigma>1) { - const int w2 = (int)(sigma - 1)/2; - const unsigned int winsize = 2*w2 + 1U; - const double frac = (sigma - winsize)/2.0; - CImg win(winsize); - Tfloat sum = 0; // window sum - for (int x = -w2; x<=w2; ++x) { - win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x); - sum+=win[x + w2]; - } - int ifirst = 0, ilast = 2*w2; - Tfloat - prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1), - next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1); - for (int x = 0; x < N - 1; ++x) { - const double sum2 = sum + frac * (prev + next); - ptr[x*off] = (T)(sum2/sigma); - prev = win[ifirst]; - sum-=prev; - ifirst = (int)((ifirst + 1)%winsize); - ilast = (int)((ilast + 1)%winsize); - win[ilast] = next; - sum+=next; - next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2); - } - const double sum2 = sum + frac * (prev + next); - ptr[(N - 1)*off] = (T)(sum2/sigma); - } - - // Derive. - switch (order) { - case 0 : - break; - case 1 : { - Tfloat - p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1), - c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0), - n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1); - for (int x = 0; x=N) return boundary_conditions?ptr[(N - 1)*off]:T(); - return ptr[x*off]; - } - - // Apply box filter of order 0,1,2. - /** - \param sigma sigma of the box filter - \param order the order of the filter 0,1 or 2. - \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& boxfilter(const float sigma, const int order, const char axis='x', - const bool boundary_conditions=true) { - if (is_empty() || !sigma || (sigma<=1 && !order)) 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; - switch (naxis) { - case 'x' : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forYZC(*this,y,z,c) - _cimg_blur_box_apply(data(0,y,z,c),nsigma,_width,1U,order,boundary_conditions); - } break; - case 'y' : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXZC(*this,x,z,c) - _cimg_blur_box_apply(data(x,0,z,c),nsigma,_height,(unsigned long)_width,order,boundary_conditions); - } break; - case 'z' : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXYC(*this,x,y,c) - _cimg_blur_box_apply(data(x,y,0,c),nsigma,_depth,(unsigned long)(_width*_height),order,boundary_conditions); - } break; - default : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) -#endif - cimg_forXYZ(*this,x,y,z) - _cimg_blur_box_apply(data(x,y,z,0),nsigma,_spectrum,(unsigned long)(_width*_height*_depth), - order,boundary_conditions); - } - } - return *this; - } - - // Apply box filter of order 0,1 or 2 \newinstance. - CImg get_boxfilter(const float sigma, const int order, const char axis='x', - const bool boundary_conditions=true) const { - return CImg(*this,false).boxfilter(sigma,order,axis,boundary_conditions); - } - - //! Blur image with a box filter. - /** - \param sigma_x Size of the box window, along the X-axis. - \param sigma_y Size of the box window, along the Y-axis. - \param sigma_z Size of the box window, along the Z-axis. - \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. - \note - - This is a recursive algorithm, not depending on the values of the box kernel size. - \see blur(). - **/ - CImg& blur_box(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true) { - if (is_empty()) return *this; - if (_width>1) boxfilter(sigma_x,0,'x',boundary_conditions); - if (_height>1) boxfilter(sigma_y,0,'y',boundary_conditions); - if (_depth>1) boxfilter(sigma_z,0,'z',boundary_conditions); - return *this; - } - - //! Blur image with a box filter \newinstance. - CImg get_blur_box(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true) const { - return CImg(*this,false).blur_box(sigma_x,sigma_y,sigma_z,boundary_conditions); - } - - //! Blur image with a box filter. - /** - \param sigma Size of the box window. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a - \see deriche(), vanvliet(). - **/ - CImg& blur_box(const float sigma, const bool boundary_conditions=true) { - const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; - return blur_box(nsigma,nsigma,nsigma,boundary_conditions); - } - - //! Blur image with a box filter \newinstance. - CImg get_blur_box(const float sigma, const bool boundary_conditions=true) const { - return CImg(*this,false).blur_box(sigma,boundary_conditions); - } - - //! Blur image, with the image guided filter. - /** - \param guide Image used to guide the smoothing process. - \param radius Spatial radius. - \param regularization Regularization parameter. - - \note This method implements the filtering algorithm described in: - He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence, - IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013 - **/ - template - CImg& blur_guided(const CImg& guide, const float radius, const float regularization) { - return get_blur_guided(guide,radius,regularization).move_to(*this); - } - - //! Blur image, with the image guided filter \newinstance. - template - CImg get_blur_guided(const CImg& guide, const float radius, const float regularization) const { - if (!is_sameXYZ(guide)) - throw CImgArgumentException(_cimg_instance - "blur_guided(): 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() || !radius) return *this; - const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100); - const unsigned int psize = (unsigned int)(1 + 2*_radius); - const CImg N = CImg(_width,_height,_depth,1,1)._blur_guided(psize); - CImg - mean_I = CImg(guide,false)._blur_guided(psize).div(N), - mean_p = CImg(*this,false)._blur_guided(psize).div(N), - cov_Ip = CImg(*this,false).mul(guide)._blur_guided(psize).div(N)-=mean_p.get_mul(mean_I), - var_I = CImg(guide,false).sqr()._blur_guided(psize).div(N)-=mean_I.get_sqr(), - &a = cov_Ip.div(var_I+=regularization), - &b = mean_p-=a.get_mul(mean_I); - a._blur_guided(psize).div(N); - b._blur_guided(psize).div(N); - return a.mul(guide)+=b; - } - - // [internal] Perform box filter with dirichlet boundary conditions. - CImg& _blur_guided(const unsigned int psize) { - const int p1 = (int)psize/2, p2 = (int)psize - p1; - if (_depth!=1) { - CImg cumul = get_cumulate('z'), cumul2 = cumul.get_shift(0,0,p2,0,1); - (cumul.shift(0,0,-p1,0,1)-=cumul2).move_to(*this); - } - if (_height!=1) { - CImg cumul = get_cumulate('y'), cumul2 = cumul.get_shift(0,p2,0,0,1); - (cumul.shift(0,-p1,0,0,1)-=cumul2).move_to(*this); - } - if (_width!=1) { - CImg cumul = get_cumulate('x'), cumul2 = cumul.get_shift(p2,0,0,0,1); - (cumul.shift(-p1,0,0,0,1)-=cumul2).move_to(*this); - } - return *this; - } - - //! 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; - cimg::unused(N2,N3); - 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) -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (res._width>=32 && res._height*res._depth>=4) private(P,Q) -#endif - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (res._width>=32 && res._height*res._depth>=4) firstprivate(P,Q) -#endif - 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) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q) -#endif - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q) -#endif - 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. - \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation. - **/ - CImg& blur_median(const unsigned int n, const float threshold=0) { - if (!n) return *this; - return get_blur_median(n,threshold).move_to(*this); - } - - //! Blur image with the median filter \newinstance. - CImg get_blur_median(const unsigned int n, const float threshold=0) const { - if (is_empty() || n<=1) return +*this; - CImg res(_width,_height,_depth,_spectrum); - T *ptrd = res._data; - cimg::unused(ptrd); - const int hl = (int)n/2, hr = hl - 1 + (int)n%2; - if (res._depth!=1) { // 3d - if (threshold>0) -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=16 && _height*_depth*_spectrum>=4) -#endif - cimg_forXYZC(*this,x,y,z,c) { // With threshold. - 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; - const float val0 = (float)(*this)(x,y,z,c); - CImg values(n*n*n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) - if (cimg::abs((float)(*this)(p,q,r,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; } - res(x,y,z,c) = values.get_shared_points(0,nb_values - 1).median(); - } - else -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width>=16 && _height*_depth*_spectrum>=4) -#endif - cimg_forXYZC(*this,x,y,z,c) { // Without threshold. - 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; - res(x,y,z,c) = 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) { // 2d - if (threshold>0) -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=16 && _height*_spectrum>=4) -#endif - cimg_forXYC(*this,x,y,c) { // With threshold. - 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; - const float val0 = (float)(*this)(x,y,c); - CImg values(n*n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) - if (cimg::abs((float)(*this)(p,q,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; } - res(x,y,c) = values.get_shared_points(0,nb_values - 1).median(); - } - else switch (n) { // Without threshold. - case 3 : { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - T I[9] = { 0 }; - CImg_3x3(J,T); - 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); - res(x,y,c) = Jcc; - } - } - } break; - case 5 : { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - T I[25] = { 0 }; - CImg_5x5(J,T); - 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); - res(x,y,c) = Jcc; - } - } - } break; - default : { -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=16 && _height*_spectrum>=4) -#endif - 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; - res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); - } - } - } - } else { // 1d - - CImg I; - if (threshold>0) -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width>=16 && _spectrum>=2) -#endif - cimg_forXC(*this,x,c) { // With threshold. - const int - x0 = x - hl, x1 = x + hr, - nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; - const float val0 = (float)(*this)(x,c); - CImg values(n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inX(*this,nx0,nx1,p) - if (cimg::abs((float)(*this)(p,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,c); ++nb_values; } - res(x,c) = values.get_shared_points(0,nb_values - 1).median(); - } - else switch (n) { // Without threshold. - case 2 : { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - I.assign(4); - cimg_for2x2(*this,x,y,0,c,I,T) res(x,c) = (T)(0.5f*(I[0] + I[1])); - } - } break; - case 3 : { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - I.assign(9); - cimg_for3x3(*this,x,y,0,c,I,T) - res(x,c) = I[3]=16 && _spectrum>=2) -#endif - cimg_forXC(*this,x,c) { - const int - x0 = x - hl, x1 = x + hr, - nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; - res(x,c) = 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 velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); - - if (_depth>1) { // 3d - if (sharpen_type) { // Shock filters. - CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); - if (sigma>0) G.blur(sigma); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width>=32 && _height*_depth>=16) -#endif - cimg_forYZ(G,y,z) { - Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1), - *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3); - CImg val, vec; - cimg_forX(G,x) { - 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); - } - } -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=512 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3x3(I,Tfloat); - 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; - } - _veloc_max[c] = veloc_max; - } - } else // Inverse diffusion. - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - 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; - } - _veloc_max[c] = veloc_max; - } - } else { // 2d. - if (sharpen_type) { // Shock filters. - CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); - if (sigma>0) G.blur(sigma); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width>=32 && _height>=16) -#endif - cimg_forY(G,y) { - CImg val, vec; - Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2); - cimg_forX(G,x) { - 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); - } - } -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height>=512 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3(I,Tfloat); - 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; - } - _veloc_max[c] = veloc_max; - } - } else // Inverse diffusion. - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - 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; - } - _veloc_max[c] = veloc_max; - } - } - const Tfloat veloc_max = _veloc_max.max(); - 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); - 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); - switch (scheme) { // 3d. - case -1 : { // Backward finite differences. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - const unsigned long off = c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Iccc - Ipcc; - *(ptrd1++) = Iccc - Icpc; - *(ptrd2++) = Iccc - Iccp; - } - } - } break; - case 1 : { // Forward finite differences. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - const unsigned long off = c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_2x2x2(I,Tfloat); - cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Incc - Iccc; - *(ptrd1++) = Icnc - Iccc; - *(ptrd2++) = Iccn - Iccc; - } - } - } break; - case 4 : { // 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 : { // 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. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - const unsigned long off = c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_3x3x3(I,Tfloat); - 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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Icc - Ipc; - *(ptrd1++) = Icc - Icp; - } - } - } break; - case 1 : { // Forward finite differences. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_2x2(I,Tfloat); - cimg_for2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Inc - Icc; - *(ptrd1++) = Icn - Icc; - } - } - } break; - case 2 : { // Sobel scheme. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - 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. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - 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_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 : { // Van Vliet filter with low standard variation - grad[0] = get_deriche(0,1,'x'); - grad[1] = get_deriche(0,1,'y'); - } break; - case 5 : { // 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - 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 = (unsigned int)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 - -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - const unsigned long off = c*_width*_height*_depth; - Tfloat - *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off, - *ptrd3 = res[3]._data + off, *ptrd4 = res[4]._data + off, *ptrd5 = res[5]._data + off; - CImg_3x3x3(I,Tfloat); - 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 -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - const unsigned long off = c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,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; - if (axis1=='x' && axis2=='x') { // Ixx - valid_axis = true; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc; - } - } - else if (axis1=='x' && axis2=='y') { // Ixy - valid_axis = true; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc; - } - } - else if (axis1=='y' && axis2=='z') { // Iyz - valid_axis = true; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - 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); - if (_depth>1) { // 3d -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; - } - } else if (_height>1) { // 2d -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; - } - } else { // 1d -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width>=1048576 && _height*_depth*_spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3(I,Tfloat); - 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 is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } - **/ - CImg& structure_tensors(const bool is_fwbw_scheme=false) { - return get_structure_tensors(is_fwbw_scheme).move_to(*this); - } - - //! Compute the structure tensor field of an image \newinstance. - CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { - if (is_empty()) return *this; - CImg res; - if (_depth>1) { // 3d - res.assign(_width,_height,_depth,6,0); - if (!is_fwbw_scheme) { // Classical central finite differences -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - 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_3x3x3(I,Tfloat); - 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; - } - } - } else { // Forward/backward finite differences. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - 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_3x3x3(I,Tfloat); - 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; - } - } - } - } else { // 2d - res.assign(_width,_height,_depth,3,0); - if (!is_fwbw_scheme) { // Classical central finite differences -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - 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_3x3(I,Tfloat); - 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; - } - } - } else { // Forward/backward finite differences (version 2). -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - 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_3x3(I,Tfloat); - 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; - } - } - } - } - 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 - get_structure_tensors().move_to(res).blur(sigma); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if(_width>=256 && _height*_depth>=256) -#endif - cimg_forYZ(*this,y,z) { - Tfloat - *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2), - *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5); - CImg val(3), vec(3,3); - cimg_forX(*this,x) { - 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 - get_structure_tensors().move_to(res).blur(sigma); -#ifdef cimg_use_openmp -#pragma omp parallel for if(_width>=256 && _height>=256) -#endif - cimg_forY(*this,y) { - Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2); - CImg val(2), vec(2,2); - cimg_forX(*this,x) { - 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)). - \param guide Image used as the initial correspondence estimate for the algorithm. - 'guide' may have a last channel with boolean values (0=false | other=true) that - tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). - **/ - 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, - const CImg& guide=CImg::const_empty()) { - return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). - 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 CImg& guide=CImg::const_empty()) 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 bool is_3d = source._depth>1; - const unsigned int constraint = is_3d?3:2; - - if (guide && - (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: - (unsigned int)cimg::round(std::log(mins/8.0)/std::log(1.5),1,1); - - 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); - - CImg U, V; - floatT bound = 0; - for (int scale = (int)_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 (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); - if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); - else { - if (guide) - guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); - 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) // Isotropic regularization. -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_height*_depth>=8 && _width>=16) reduction(+:_energy) -#endif - cimg_forYZ(U,y,z) { - const int - _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; - if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; - if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; - bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; - bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; - bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; - } else { - if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; - if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; - if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; - bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; - bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; - bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; - } - _energy+=delta_I*delta_I + smoothness*_energy_regul; - } - if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. - U(x,y,z,0) = V(x,y,z,0)/factor; - U(x,y,z,1) = V(x,y,z,1)/factor; - U(x,y,z,2) = V(x,y,z,2)/factor; - } - } else { // Anisotropic regularization. - const float nsmoothness = -smoothness; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if (_height*_depth>=8 && _width>=16) reduction(+:_energy) -#endif - cimg_forYZ(U,y,z) { - const int - _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; - if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; - if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; - bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; - bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; - bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; - } else { - if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; - if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; - if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; - bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; - bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; - bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; - } - _energy+=delta_I*delta_I + nsmoothness*_energy_regul; - } - if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. - U(x,y,z,0) = V(x,y,z,0)/factor; - U(x,y,z,1) = V(x,y,z,1)/factor; - U(x,y,z,2) = V(x,y,z,2)/factor; - } - } - } - } else { // 2d version. - if (smoothness>=0) // Isotropic regularization. -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy) -#endif - cimg_forY(U,y) { - const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; - if (U(x,y,1)>y) U(x,y,1) = (float)y; - bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; - bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; - } else { - if (U(x,y,0)<-x) U(x,y,0) = -(float)x; - if (U(x,y,1)<-y) U(x,y,1) = -(float)y; - bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; - bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; - } - _energy+=delta_I*delta_I + smoothness*_energy_regul; - } - if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints. - U(x,y,0) = V(x,y,0)/factor; - U(x,y,1) = V(x,y,1)/factor; - } - } else { // Anisotropic regularization. - const float nsmoothness = -smoothness; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy) -#endif - cimg_forY(U,y) { - const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; - if (U(x,y,1)>y) U(x,y,1) = (float)y; - bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; - bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; - } else { - if (U(x,y,0)<-x) U(x,y,0) = -(float)x; - if (U(x,y,1)<-y) U(x,y,1) = -(float)y; - bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; - bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; - } - _energy+=delta_I*delta_I + nsmoothness*_energy_regul; - } - if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints. - U(x,y,0) = V(x,y,0)/factor; - U(x,y,1) = V(x,y,1)/factor; - } - } - } - } - 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 correspondence map between two images, using the patch-match algorithm. - /** - \param patch_image The image containing the reference patches to match with the instance image. - \param patch_width Width of the patch used for matching. - \param patch_height Height of the patch used for matching. - \param patch_depth Depth of the patch used for matching. - \param nb_iterations Number of patch-match iterations. - \param nb_randoms Number of randomization attempts (per pixel). - \param guide Image used as the initial correspondence estimate for the algorithm. - 'guide' may have a last channel with boolean values (0=false | other=true) that - tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). - \param[out] matching_score Returned as the image of matching scores. - \note - The patch-match algorithm is described in this paper: - Connelly Barnes, Eli Shechtman, Adam Finkelstein, Dan B Goldman(2009), - PatchMatch: A Randomized Correspondence Algorithm for Structural Image Editing - **/ - template - CImg& patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const CImg &guide, - CImg &matching_score) { - return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,guide,matching_score).move_to(*this); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. - template - CImg get_patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const CImg &guide, - CImg &matching_score) const { - return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms, - guide,true,matching_score); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - template - CImg& patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const CImg &guide) { - return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,guide).move_to(*this); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - template - CImg get_patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const CImg &guide) const { - return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms, - guide,false,CImg::empty()); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - CImg& patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth=1, - const unsigned int nb_iterations=5, - const unsigned int nb_randoms=5) { - return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms).move_to(*this); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - CImg get_patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth=1, - const unsigned int nb_iterations=5, - const unsigned int nb_randoms=5) const { - return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms, - CImg::const_empty(), - false,CImg::empty()); - } - - template - CImg _get_patchmatch(const CImg& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const CImg &guide, - const bool is_matching_score, - CImg &matching_score) const { - if (is_empty()) return CImg::const_empty(); - if (patch_image._spectrum!=_spectrum) - throw CImgArgumentException(_cimg_instance - "patchmatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " - "have different spectrums.", - cimg_instance, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - if (patch_width>_width || patch_height>_height || patch_depth>_depth) - throw CImgArgumentException(_cimg_instance - "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions " - "of the instance image.", - cimg_instance,patch_width,patch_height,patch_depth); - if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) - throw CImgArgumentException(_cimg_instance - "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions " - "of the patch image image (%u,%u,%u,%u,%p).", - cimg_instance,patch_width,patch_height,patch_depth, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - const unsigned int - _constraint = patch_image._depth>1?3:2, - constraint = guide._spectrum>_constraint?_constraint:0; - - if (guide && - (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) - throw CImgArgumentException(_cimg_instance - "patchmatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " - "considering instance and patch image image (%u,%u,%u,%u,%p).", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - - CImg map(_width,_height,_depth,patch_image._depth>1?3:2); - CImg score(_width,_height,_depth); - const int - psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, - psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, - psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; - - if (_depth>1 || patch_image._depth>1) { // 3d version. - - // Initialize correspondence map. - if (guide) cimg_forXYZ(*this,x,y,z) { // User-defined initialization. - const int - cx1 = x<=psizew1?x:(x::inf()); - } else cimg_forXYZ(*this,x,y,z) { // Random initialization. - const int - cx1 = x<=psizew1?x:(x::inf()); - } - - // Start iteration loop. - for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. - const int u = map(x - 1,y,z,0), v = map(x - 1,y,z,1), w = map(x - 1,y,z,2); - if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0) { // Compare with up neighbor. - const int u = map(x,y - 1,z,0), v = map(x,y - 1,z,1), w = map(x,y - 1,z,2); - if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0) { // Compare with backward neighbor. - const int u = map(x,y,z - 1,0), v = map(x,y,z - 1,1), w = map(x,y,z - 1,2); - if (u>=cx1 && u=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); - } else cimg_forXY(*this,x,y) { // Random initialization. - const int - cx1 = x<=psizew1?x:(x::inf()); - } - - // Start iteration loop. - for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. - const int u = map(x - 1,y,0), v = map(x - 1,y,1); - if (u>=cx1 - 1 && u=cy1 && v0) { // Compare with up neighbor. - const int u = map(x,y - 1,0), v = map(x,y - 1,1); - if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, - const unsigned int psizew, const unsigned int psizeh, - const int x1, const int y1, - const int x2, const int y2, - const float max_ssd) { // 2d version. - const T *p1 = img1.data(x1,y1), *p2 = img2.data(x2,y2); - const unsigned long - offx1 = (unsigned long)img1._width - psizew, - offx2 = (unsigned long)img2._width - psizew, - offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width, - offy2 = (unsigned long)img2._width*img2._height - psizeh*img2._width; - float ssd = 0; - cimg_forC(img1,c) { - for (unsigned int j = 0; jmax_ssd) return max_ssd; - p1+=offx1; p2+=offx2; - } - p1+=offy1; p2+=offy2; - } - return ssd; - } - - static float _patchmatch(const CImg& img1, const CImg& img2, - const unsigned int psizew, const unsigned int psizeh, const unsigned int psized, - const int x1, const int y1, const int z1, - const int x2, const int y2, const int z2, - const float max_ssd) { // 3d version. - const T *p1 = img1.data(x1,y1,z1), *p2 = img2.data(x2,y2,z2); - const unsigned long - offx1 = (unsigned long)img1._width - psizew, - offx2 = (unsigned long)img2._width - psizew, - offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width - psizew, - offy2 = (unsigned long)img2._width*img2._height - psizeh*img2._width - psizew, - offz1 = (unsigned long)img1._width*img1._height*img1._depth - psized*img1._width*img1._height - - psizeh*img1._width - psizew, - offz2 = (unsigned long)img2._width*img2._height*img2._depth - psized*img2._width*img2._height - - psizeh*img2._width - psizew; - float ssd = 0; - cimg_forC(img1,c) { - for (unsigned int k = 0; kmax_ssd) return max_ssd; - p1+=offx1; p2+=offx2; - } - p1+=offy1; p2+=offy2; - } - p1+=offz1; p2+=offz2; - } - return ssd; - } - - //! 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; - if (cimg::type::string()!=cimg::type::string()) // For datatype < int. - return CImg(*this,false).distance((Tint)value,metric). - cut((Tint)cimg::type::min(),(Tint)cimg::type::max()).move_to(*this); - bool is_value = false; - cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)cimg::max(0,99999999); // Trick to avoid VC++ warning - 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)) { - // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why. -#define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9) - - const unsigned long wh = (unsigned long)_width*_height; -#if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - CImg g(_width), dt(_width), s(_width), t(_width); - CImg img = get_shared_channel(c); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) firstprivate(g,dt,s,t) -#endif - 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]; - } - if (_height>1) { - g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for collapse(2) if (_height>=512 && _width*_depth>=16) firstprivate(g,dt,s,t) -#endif - 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); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for collapse(2) if (_depth>=512 && _width*_height>=16) firstprivate(g,dt,s,t) -#endif - 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; -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) -#endif - cimg_forC(*this,c) { - CImg img = get_shared_channel(c); -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(3) if (_width*_height*_depth>=1024) -#endif - 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. - -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state) -#endif - 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.0f); - 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 : - throw CImgArgumentException("CImgList<%s>::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); - } - 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::const_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) = (int)(((*this)(p,0) - xmin)*texture._width/dx); - _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/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]; - const int x0 = _coords(i0,0), y0 = _coords(i0,1); - texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, - y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]); - } break; - case 2 : case 6 : { // Line. - const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - 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]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1), - x2 = _coords(i2,0), y2 = _coords(i2,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - 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]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1), - x2 = _coords(i2,0), y2 = _coords(i2,1), - x3 = _coords(i3,0), y3 = _coords(i3,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - 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?((*this)(x,y,1) - m)*255/(M-m):r), - b = (unsigned char)(_spectrum>2?((*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 = (unsigned int)*(segment++), p1 = (unsigned int)*(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 = (unsigned int)*(triangle++), - p1 = (unsigned int)*(triangle++), - p2 = (unsigned int)*(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(expr,0,CImg::const_empty(),0); - } - ~_functor2d_expr() { delete mp; } - float operator()(const float x, const float y) const { - return (float)(*mp)(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(expr,0,CImg::const_empty(),0); - } - float operator()(const float x, const float y, const float z) const { - return (float)(*mp)(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 { - CImg error_message(1024); - 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.data()); - 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 = 8U + 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 = 8U + 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 { - CImg error_message(1024); - if (!is_CImg3d(full_check,error_message)) - throw CImgInstanceException(_cimg_instance - "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", - cimg_instance,error_message.data()); - 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?(unsigned long)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 = (unsigned int)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 - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._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; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - 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; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - 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; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - 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; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - 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 - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - 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 - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - } - 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; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - 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<=0?0:x0>=texture.width()?texture.width() - 1:x0, - y0<=0?0:y0>=texture.height()?texture.height() - 1: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 = 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 = xleft<=xright?zbuffer.data(xleft,y):0; - 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 = 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 = 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 = xleft<=xright?zbuffer.data(xleft,y):0; - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY(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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY((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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - 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._atXY((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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - lwh = (unsigned long)light._width*light._height, - 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; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval)); - ptrd+=whd; lig+=lwh; - } - 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; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval)); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; lig+=lwh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - lwh = (unsigned long)light._width*light._height, - 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 = xleft<=xright?zbuffer.data(xleft,y):0; - if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const tc cval = *(col++); - *ptrd = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval); - ptrd+=whd; lig+=lwh; - } - 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; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - 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; lig+=lwh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - ptrd+=whd; col+=twh; - } - 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._atXY(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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY((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+=twh; - } - 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._atXY((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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - 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._atXY((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+=twh; - } - 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._atXY((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+=twh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - lwh = (unsigned long)light._width*light._height, - 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._atXY(txleft,tyleft); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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._atXY(txleft,tyleft); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - lwh = (unsigned long)light._width*light._height, - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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 unsigned long - whd = (unsigned long)_width*_height*_depth, - twh = (unsigned long)texture._width*texture._height, - lwh = (unsigned long)light._width*light._height, - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - 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 = (int)(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*(uptrT)sprite.width():0) + - (bz?-z0*(uptrT)sprite.width()*sprite.height():0) + - (bc?-c0*(uptrT)sprite.width()*sprite.height()*sprite.depth():0); - const uptrT - 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*(uptrT)sprite.width():0) + - (bz?-z0*(uptrT)sprite.width()*sprite.height():0) + - (bc?-c0*(uptrT)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 uptrT - coff = (bx?-x0:0) + - (by?-y0*(uptrT)mask.width():0) + - (bz?-z0*(uptrT)mask.width()*mask.height():0) + - (bc?-c0*(uptrT)mask.width()*mask.height()*mask.depth():0), - ssize = (uptrT)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; - CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,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; - CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,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; - CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,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; - CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,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); - CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); - } - - //! 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; - CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); - } - - 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 = (int)(x + u), yy = (int)(y + 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 - (int)font_height; - const int siz = (int)values_x.size() - 1; - CImg txt(32); - CImg label; - if (siz<=0) { // Degenerated case. - draw_line(0,y,_width - 1,y,color,opacity,pattern); - if (!siz) { - cimg_snprintf(txt,txt._width,"%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' || 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' || 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; - CImg txt(32); - CImg label; - if (siz<=0) { // Degenerated case. - draw_line(x,0,x,_height - 1,color,opacity,pattern); - if (!siz) { - cimg_snprintf(txt,txt._width,"%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' || 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' || 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?1U:0U; 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?1U:0U; 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 { - const float fx = (float)_width/siz1; - 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); - const float fx = (float)_width/siz1; - int oX = 0; - cimg_foroff(data,off) { - const int - X = (int)((off + 1)*fx) - 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; - const float fx = (float)_width1/siz1; - switch (vertex_type%8) { - case 1 : { // Point - cimg_foroff(data,off) { - const int - X = (int)(off*fx + 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 = (int)(off*fx + 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 = (int)(off*fx + 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 = (int)(off*fx + 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 = (int)(off*fx + 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 = (int)(off*fx + 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 = (int)(off*fx + 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) = (unsigned int)x; - remaining(1,0) = (unsigned int)y; - remaining(2,0) = (unsigned int)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); - const int - _x0 = x0<0?0:x0>=width()?width() - 1:x0, - _y0 = y0<0?0:y0>=height()?height() - 1:y0, - _x1 = x1<0?1:x1>=width()?width() - 1:x1, - _y1 = y1<0?1:y1>=height()?height() - 1:y1; -#ifdef cimg_use_openmp -#pragma omp parallel for collapse(2) if ((1 + _x1 - _x0)*(1 + _y1 - _y0)>=2048) -#endif - for (int q = _y0; q<=_y1; ++q) - for (int p = _x0; p<=_x1; ++p) { - unsigned int iteration = 0; - 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::const_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::const_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::const_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::const_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 - static float ___draw_object3d(const CImgList& opacities, const unsigned int n_primitive) { - return n_primitive - static float ___draw_object3d(const CImg& opacities, const unsigned int n_primitive) { - return n_primitive - 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; - typedef typename to::value_type _to; - if (is_empty() || !vertices || !primitives) return *this; - CImg error_message(1024); - 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.data()); -#ifndef cimg_use_board - if (pboard) return *this; -#endif - if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety. - - 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) { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(projections.size()>4096) -#endif - 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 { -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(projections.size()>4096) -#endif - 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,1,1,1,~0U); - CImg zrange(primitives._width); - const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); - bool is_forward = zbuffer?true:false; - -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(primitives.size()>4096) -#endif - cimglist_for(primitives,l) { - const CImg& primitive = primitives[l]; - switch (primitive.size()) { - case 1 : { // Point - CImg<_to> _opacity; - __draw_object3d(opacities,l,_opacity); - if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false; - const unsigned int i0 = (unsigned int)primitive(0); - const tpfloat z0 = Z + vertices(i0,2); - if (z0>zmin) { - visibles(l) = (unsigned int)l; - zrange(l) = 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(l) = (unsigned int)l; - zrange(l) = _zc; - } - is_forward = false; - } 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(l) = (unsigned int)l; - zrange(l) = (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(l) = (unsigned int)l; - zrange(l) = (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(l) = (unsigned int)l; - zrange(l) = (z0 + z1 + z2 + z3)/4; - } - } - } break; - default : - if (render_type==5) cimg::mutex(10,0); - 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()); - } - } - - // Force transparent primitives to be drawn last when zbuffer is activated - // (and if object contains no spheres or sprites). - if (is_forward) - cimglist_for(primitives,l) - if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); - - // Sort only visibles primitives. - unsigned int *p_visibles = visibles._data; - tpfloat *p_zrange = zrange._data; - const tpfloat *ptrz = p_zrange; - cimg_for(visibles,ptr,unsigned int) { - if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; } - ++ptrz; - } - const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); - if (!nb_visibles) { - if (render_type==5) cimg::mutex(10,0); - return *this; - } - CImg permutations; - CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); - - // Compute light properties - CImg lightprops; - switch (render_type) { - case 3 : { // Flat Shading - lightprops.assign(nb_visibles); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) -#endif - 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,6,1,1,0); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) -#endif - 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; - unsigned int ix = 0, iy = 1, iz = 2; - if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; } - vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz; - vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz; - vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz; - if (rectangle_flag) { - vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; - } - } - } - - if (is_double_sided) cimg_forX(vertices_normals,p) { - const float - nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2), - nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5), - n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1; - if (n1>n0) { - vertices_normals(p,0) = -nx1; - vertices_normals(p,1) = -ny1; - vertices_normals(p,2) = -nz1; - } - } - - if (render_type==4) { - lightprops.assign(vertices._width); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) -#endif - 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); -#ifdef cimg_use_openmp -#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) -#endif - 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); - 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], - is_wireframe = (unsigned int)primitive[2]; - 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 : - if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); - else 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)); - if (!is_wireframe) board.fillCircle(xc,height() - yc,radius); - else { - board.setFillColor(LibBoard::Color::None); - board.drawCircle(xc,height() - yc,radius); - } - } -#endif - break; - } - } break; - case 6 : { // Textured line - if (!__color) { - if (render_type==5) cimg::mutex(10,0); - 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]; - const int - tx0 = (int)primitive[2], ty0 = (int)primitive[3], - tx1 = (int)primitive[4], ty1 = (int)primitive[5], - 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<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1: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) { - if (render_type==5) cimg::mutex(10,0); - 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]; - const int - tx0 = (int)primitive[3], ty0 = (int)primitive[4], - tx1 = (int)primitive[5], ty1 = (int)primitive[6], - tx2 = (int)primitive[7], ty2 = (int)primitive[8], - 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<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). - draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, - ty2<=0?0:ty2>=color.height()?color.height() - 1: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) { - if (render_type==5) cimg::mutex(10,0); - 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]; - const int - tx0 = (int)primitive[4], ty0 = (int)primitive[5], - tx1 = (int)primitive[6], ty1 = (int)primitive[7], - tx2 = (int)primitive[8], ty2 = (int)primitive[9], - tx3 = (int)primitive[10], ty3 = (int)primitive[11], - 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<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). - draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, - ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity). - draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3, - ty3<=0?0:ty3>=color.height()?color.height() - 1: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, - const bool exit_on_anykey=false) { - return get_select(disp,feature_type,XYZ,exit_on_anykey).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, - const bool exit_on_anykey=false) { - return get_select(title,feature_type,XYZ,exit_on_anykey).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 bool exit_on_anykey=false) const { - return _get_select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,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 bool exit_on_anykey=false) const { - CImgDisplay disp; - return _get_select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,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 exit_on_anykey, - 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); - - CImg thumb; - if (width()>disp.screen_width() || height()>disp.screen_height()) { - const double ratio = cimg::min((double)disp.screen_width()/width(),(double)disp.screen_height()/height()); - get_resize(cimg::max(1,(int)(ratio*width())),cimg::max(1,(int)(ratio*height())),-100,-100).move_to(thumb); - } - - 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(); - - static const 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; - CImg text(1024); *text = 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; - - CImg filename(32); - - 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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data). - 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; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,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._data). - 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) { - 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; } - // Left + right buttons: reset. - if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } - 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. - if (thumb) thumb.__get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); - else __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); - 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 mX = (int)X; - int mY = (int)Y; - int mZ = (int)Z; - int w = disp.width(), W = width() + d, - h = disp.height(), H = height() + d, - _xp = (int)(mX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=mX?1:0), - _yp = (int)(mY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=mY?1:0), - _xn = (int)((mX + 1.0f)*w/W - 1), xn = _xn + ((int)((_xn + 1.0f)*W/w)!=mX + 1?1:0), - _yn = (int)((mY + 1.0f)*h/H - 1), yn = _yn + ((int)((_yn + 1.0f)*H/h)!=mY + 1?1:0), - _zxp = (int)((mZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=mZ + width()?1:0), - _zyp = (int)((mZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=mZ + height()?1:0), - _zxn = (int)((mZ + width() + 1.0f)*w/W - 1), - zxn = _zxn + ((int)((_zxn + 1.0f)*W/w)!=mZ + width() + 1?1:0), - _zyn = (int)((mZ + height() + 1.0f)*h/H - 1), - zyn = _zyn + ((int)((_zyn + 1.0f)*H/h)!=mZ + height() + 1?1:0), - _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.0f)*W/w)!=width()?1:0), - _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.0f)*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 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0?1:0), - _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0?1:0), - _xn0 = (int)((X0 + 1.0f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.0f)*W/w)!=X0 + 1?1:0), - _yn0 = (int)((Y0 + 1.0f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.0f)*H/h)!=Y0 + 1?1:0), - _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()?1:0), - _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()?1:0), - _zxn0 = (int)((Z0 + width() + 1.0f)*w/W - 1), - zxn0 = _zxn0 + ((int)((_zxn0 + 1.0f)*W/w)!=Z0 + width() + 1?1:0), - _zyn0 = (int)((Z0 + height() + 1.0f)*h/H - 1), - zyn0 = _zyn0 + ((int)((_zyn0 + 1.0f)*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,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); - else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); - char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; - for (unsigned int c = 0; c<_spectrum && ctext::format(), - cimg::type::format((*this)((int)X,(int)Y,(int)Z,c))); - ctext = text._data + std::strlen(text); - *(ctext++) = ' '; *ctext = 0; - } - std::strcpy(text._data + 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,text._width," 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,text._width," 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,text._width," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ", - origX + (X01 || force_display_z_coord) - cimg_snprintf(text,text._width," 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,text._width," 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; - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - - // 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; - } - } - if (!exit_on_anykey || !(disp.button()&4)) 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); - - // Check for inf and NaN values. - if (cimg::type::is_float() && normalization) { - 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) { - Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::min(); - if (!normalization) { m0 = 0; M0 = 255; } - else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } - else - cimg_for(img2d,ptr,Tuchar) - if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { - if (*ptr<(Tuchar)m0) m0 = *ptr; - if (*ptr>(Tuchar)M0) M0 = *ptr; - } - const T - val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), - val_pinf = (T)(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 bool exit_on_anykey=false) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "select_graph(): Empty instance.", - cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,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; } - - static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; - static 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?0U:1U; - unsigned int okey = 0, obutton = 0; - CImg message(1024); - 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) = (unsigned char)((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 = (int)(16 + nx0*(visu.width() - 32)/cimg::max(1U,siz - one)), - sx1 = (int)(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,message._width,"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,message._width,"Value[%u:%g] = ( ",x,cx); - cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - cimg_sprintf(message._data + std::strlen(message),")"); - } - if (x0>=0 && x1>=0) { - const unsigned int - nx0 = (unsigned int)(x0<=x1?x0:x1), - nx1 = (unsigned int)(x0<=x1?x1:x0), - ny0 = (unsigned int)(y0<=y1?y0:y1), - ny1 = (unsigned int)(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) - cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", - x0,cx0,cy0,x1 + one,cx1,cy1); - else - cimg_sprintf(message._data + 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. - CImg filename(32); - 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(CImgDisplay::screen_width()/2, - CImgDisplay::screen_height()/2,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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,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._data).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(); - if (!exit_on_anykey && okey && okey!=cimg::keyESC && - (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - disp.set_key(key,false); - okey = 0; - } - } - - 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)) { - CImg filename_local(256); - load(cimg::load_network(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,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !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_video(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::ftype(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,"inr")) load_inr(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,"gif")) load_gif_external(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"); - CImg line(256); *line = 0; - int err = std::fscanf(nfile,"%255[^\n]",line._data); - unsigned int dx = 0, dy = 1, dz = 1, dc = 1; - cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); - err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]"); - 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"); - CImg delimiter(256), tmp(256); *delimiter = *tmp = 0; - unsigned int cdx = 0, dx = 0, dy = 0; - int err = 0; - double val; - assign(256,256,1,1,0); - while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) { - if (err>0) (*this)(cdx++,dy) = (T)val; - if (cdx>=_width) resize(3*_width/2,_height,1,1,0); - char c = 0; - if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&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"); - CImg header(54); - cimg::fread(header._data,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 = 24*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; - } - } - } - return *this; -#else - throw CImgIOException(_cimg_instance - "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", - cimg_instance, - filename); -#endif - } - - //! 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 = 24*1024*1024; - while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (cimg_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=cimg_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 (cimg_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 (cimg_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=cimg_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 (cimg_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 = 24*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 = 24*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 compile 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, - float *const voxel_size=0, - CImg *const description=0) { - 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,voxel_size,description); - 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, - float *const voxel_size=0, - CImg *const description=0) { - return CImg().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); - } - - // (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, - float *const voxel_size, CImg *const description) { - if (!TIFFSetDirectory(tif,directory)) return assign(); - uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; - uint16 sampleformat = 1; - uint32 nx = 1, ny = 1; - const char *const filename = TIFFFileName(tif); - const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); - TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); - TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); - TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); - TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); - TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); - if (voxel_size) { - const char *s_description = 0; - float vx = 0, vy = 0, vz = 0; - if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) { - const char *s_desc = std::strstr(s_description,"VX="); - if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format. - voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz; - } - s_desc = std::strstr(s_description,"spacing="); - if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format. - voxel_size[2] = vz; - } - } - TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size); - TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1); - voxel_size[0] = 1.0f/voxel_size[0]; - voxel_size[1] = 1.0f/voxel_size[1]; - } - if (description) { - const char *s_description = 0; - if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) - CImg::string(s_description).move_to(*description); - } - const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; - assign(nx,ny,1,spectrum); - - if ((photo>=3 && sampleformat==1 && - (bitspersample==4 || bitspersample==8) && - (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || - (bitspersample==1 && samplesperpixel==1)) { - // Special case for unsigned color images. - 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,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); - 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); - } else { // Other cases. - uint16 config; - TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); - if (TIFFIsTiled(tif)) { - uint32 tw = 1, th = 1; - 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; - } - } - } - 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) { - CImg body(1024); - 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"); - cimg_sprintf(body._data + 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"); - cimg_sprintf(body._data + 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-size 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 = *(unsigned 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[(unsigned int)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[(unsigned 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 8 : { - int *const buffer = new int[(unsigned 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[(unsigned 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 64 : { - double *const buffer = new double[(unsigned 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; - 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) { - CImg item(1024), tmp1(64), tmp2(64); - *item = *tmp1 = *tmp2 = 0; - out[0] = std::fscanf(file,"%63s",item._data); - 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._data)!=EOF && std::strncmp(item,"##}",3)) { - cimg_sscanf(item," XDIM%*[^0-9]%d",out); - cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1); - cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2); - cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3); - cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6); - if (voxel_size) { - cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size); - cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); - cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); - } - if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; - switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { - case 0 : break; - case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,tmp1._width - 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._data); - } - } - 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[(unsigned int)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"); - CImg header(32); - cimg::fread(header._data,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 }; - int ptbuf[4] = { 0 }; - cimg::fread(&imageid,1,nfile); - const bool endian = imageid>255; - if (endian) cimg::invert_endianness(imageid); - cimg::fread(header._data,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 - cimg::fread(ptbuf,1,nfile); - if (endian) cimg::invert_endianness(ptbuf,1); - assign(1); (*this)(0) = (T)ptbuf[0]; - } break; - case 35 : { // Points 2d - 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 - 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); - if (cimg::is_directory(filename)) - throw CImgArgumentException(_cimg_instance - "load_raw(): Specified filename '%s' is a directory.", - cimg_instance,filename); - - 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 (siz && (!is_multiplexed || size_c==1)) { - cimg::fread(_data,siz,nfile); - if (invert_endianness) cimg::invert_endianness(_data,siz); - } else if (siz) { - 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 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; - CImg line(256); *line = 0; - int err; - - // Skip comments, and read magic string OFF - do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } 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._data); } while (!err || (err==1 && *line=='#')); - if ((err = cimg_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._data); } while (!err || (err==1 && *line=='#')); - if ((err = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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._data))<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 = cimg_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 from a video file, using OpenCV library. - /** - \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. - **/ - CImg& load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - const char axis='z', const float align=0) { - return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this); - } - - //! Load image sequence from a video file, using OpenCV library \newinstance. - static CImg get_load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - const char axis='z', const float align=0) { - return CImgList().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align); - } - - //! 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. - CImg command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg s_filename = CImg::string(filename)._system_strescape(); -#if cimg_OS==1 - cimg_snprintf(command,command._width,"%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(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"", - cimg::graphicsmagick_path(),s_filename.data(), - CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::graphicsmagick_path()); - if (!(file = std::fopen(filename_tmp,"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(filename_tmp); - std::remove(filename_tmp); - 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. - CImg command(1024), filename_tmp(256), body(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gunzip_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - if (!(file = std::fopen(filename_tmp,"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(filename_tmp); - std::remove(filename_tmp); - 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. - CImg command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg s_filename = CImg::string(filename)._system_strescape(); -#if cimg_OS==1 - cimg_snprintf(command,command._width,"%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(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s%s \"%s\" \"%s\"", - cimg::imagemagick_path(), - !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::imagemagick_path()); - if (!(file = std::fopen(filename_tmp,"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(filename_tmp); - std::remove(filename_tmp); - 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. - CImg command(1024), filename_tmp(256), body(256); - cimg::fclose(cimg::fopen(filename,"r")); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"", - cimg::medcon_path(), - CImg::string(filename_tmp)._system_strescape().data(), - CImg::string(filename)._system_strescape().data()); - cimg::system(command); - cimg::split_filename(filename_tmp,body); - - cimg_snprintf(command,command._width,"%s.hdr",body._data); - file = std::fopen(command,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); - 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,command._width,"%s.img",body._data); - 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. - CImg command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg s_filename = CImg::string(filename)._system_strescape(); -#if cimg_OS==1 - cimg_snprintf(command,command._width,"%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(filename_tmp,filename_tmp._width,"%s%c%s.ppm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"", - cimg::dcraw_path(),s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::dcraw_path()); - if (!(file = std::fopen(filename_tmp,"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(filename_tmp); - std::remove(filename_tmp); - 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=true, const unsigned int capture_width=0, - const unsigned int capture_height=0) { -#ifdef cimg_use_opencv - if (camera_index>99) - throw CImgArgumentException(_cimg_instance - "load_camera(): Invalid request for camera #%u " - "(no more than 100 cameras can be managed simultaneously).", - cimg_instance, - camera_index); - static CvCapture *capture[100] = { 0 }; - static unsigned int capture_w[100], capture_h[100]; - if (release_camera) { - cimg::mutex(9); - if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index])); - capture[camera_index] = 0; - capture_w[camera_index] = capture_h[camera_index] = 0; - cimg::mutex(9,0); - return *this; - } - if (!capture[camera_index]) { - cimg::mutex(9); - capture[camera_index] = cvCreateCameraCapture(camera_index); - capture_w[camera_index] = 0; - capture_h[camera_index] = 0; - cimg::mutex(9,0); - if (!capture[camera_index]) { - throw CImgIOException(_cimg_instance - "load_camera(): Failed to initialize camera #%u.", - cimg_instance, - camera_index); - } - } - cimg::mutex(9); - if (capture_width!=capture_w[camera_index]) { - cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width); - capture_w[camera_index] = capture_width; - } - if (capture_height!=capture_h[camera_index]) { - cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height); - capture_h[camera_index] = 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++); - } - } - cimg::mutex(9,0); - return *this; -#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 - } - - //! 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=true, - 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 information 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?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; - - CImg _title(64); - if (!title) cimg_snprintf(_title,_title._width,"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._data,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 information are displayed on the standard output. - **/ - const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) const { - return _display(disp,0,display_info,XYZ,exit_on_anykey,false); - } - - //! Display image into an interactive window. - /** - \param title Window title - \param display_info Tells if image information 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 bool exit_on_anykey=false) const { - CImgDisplay disp; - return _display(disp,title,display_info,XYZ,exit_on_anykey,false); - } - - const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info, - unsigned int *const XYZ, const bool exit_on_anykey, - const bool exit_on_simpleclick) const { - unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; - int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, - old_mouse_x = -1, old_mouse_y = -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] = (unsigned int)(x0 + x1)/2; - _XYZ[1] = (unsigned int)(y0 + y1)/2; - _XYZ[2] = (unsigned int)(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 = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0, - tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U); - 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] = (unsigned int)(x1 - x0)/2; - _XYZ[1] = (unsigned int)(y1 - y0)/2; - _XYZ[2] = (unsigned int)(z1 - z0)/2; - } - - disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; - const CImg selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1); - old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; - is_first_select = false; - - if (disp.wheel()) { - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - go_down = !(go_up = disp.wheel()>0); - } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { - go_left = !(go_right = disp.wheel()>0); - } - else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { - go_out = !(go_in = disp.wheel()>0); go_in_center = false; - } - 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 = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; - if (mX=height()) { - X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); Y = (int)_XYZ[1]; - } - if (mX>=width() && mY4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } - if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } - if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; } - } - 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)/4, 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)/4, 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)/4, 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)/4, 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) 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,exit_on_anykey); - } - - 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 bool exit_on_anykey) 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,exit_on_anykey); - else return CImg(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2, - CImgDisplay::screen_height()/2,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,exit_on_anykey); - } else { if (disp) disp.resize(*this,false); } - CImg error_message(1024); - 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.data()); - if (vertices._width && !primitives) { - CImgList nprimitives(vertices._width,1,1,1,1); - cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)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,exit_on_anykey); - } - 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 - //#ifdef cimg_use_openmp - // const bool render_with_zbuffer = true; - //#else - const bool render_with_zbuffer = !clicked && nrender_static>0; - //#endif - 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,render_with_zbuffer?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; } - - CImg filename(32); - 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 = 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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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 (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - 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 bool exit_on_anykey=false) const { - return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); - } - - //! 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 bool exit_on_anykey=false) const { - CImgDisplay disp; - return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); - } - - 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 bool exit_on_anykey=false) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "display_graph(): Empty instance.", - cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,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; !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,true); - 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 = (int)disp.key()) { - case cimg::keyHOME : reset_view = 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_up = !(go_down = disp.wheel()<0); - else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); - else go_out = !(go_in = 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 = (int)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; - } - } - if (!exit_on_anykey && key && key!=(int)cimg::keyESC && - (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - disp.set_key(key,false); - key = 0; - } - } - 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 bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); - const char *const ext = cimg::split_filename(filename); - CImg nfilename(1024); - const char *const fn = is_stdout?filename:(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,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !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_video(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"); - CImg varname(1024); *varname = 0; - if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); - if (!*varname) cimg_snprintf(varname,varname._width,"unnamed"); - std::fprintf(nfile, - "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" - "%s data_%s[] = { %s\n ", - varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data, - 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"); - CImg header(54,1,1,1,0); - unsigned char 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._data,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); - return *this; -#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 - } - - //! 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"); - volatile 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 = 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 = 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 = 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 = 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 = 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 = 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) // Can be saved as regular PNM file. - _save_pnm(file,filename,0); - 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 = 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 = 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 = 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 { - get_mirror('y')._save_pfm(0,filename); - return *this; - } - - //! Save image as a PFM file \overloading. - const CImg& save_pfm(std::FILE *const file) const { - get_mirror('y')._save_pfm(file,0); - return *this; - } - - 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 = 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 = 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 = 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{ 0=None | 1=LZW | 2=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 compile time the - function uses CImg&save_other(const char*). - **/ - const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, - const float *const voxel_size=0, const char *const description=0, - const bool use_bigtiff=true) 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 - const bool _use_bigtiff = use_bigtiff && sizeof(uptrT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images. - TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); - if (tif) { - cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); - TIFFClose(tif); - } else throw CImgIOException(_cimg_instance - "save_tiff(): Failed to open file '%s' for writing.", - cimg_instance, - filename); - return *this; -#else - cimg::unused(compression_type,voxel_size,description,use_bigtiff); - return save_other(filename); -#endif - } - -#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,z,foo,compression_type,voxel_size,description); } - - // [internal] Save a plane into a tiff file - template - const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, - const unsigned int compression_type, const float *const voxel_size, - const char *const description) 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); - if (voxel_size) { - const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2]; - TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE); - TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.0f/vx); - TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.0f/vy); - CImg s_description(256); - cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz); - TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data()); - } - if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description); - 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==2?COMPRESSION_JPEG: - compression_type==1?COMPRESSION_LZW: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 z, - const unsigned int compression_type, const float *const voxel_size, - const char *const description) 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; - CImg header(348,1,1,1,0), hname(1024), iname(1024); - const char *const ext = cimg::split_filename(filename); - short datatype = -1; - if (!*ext) { - cimg_snprintf(hname,hname._width,"%s.hdr",filename); - cimg_snprintf(iname,iname._width,"%s.img",filename); - } - if (!cimg::strncasecmp(ext,"hdr",3)) { - std::strcpy(hname,filename); - std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); - } - if (!cimg::strncasecmp(ext,"img",3)) { - std::strcpy(hname,filename); - std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); - } - if (!cimg::strncasecmp(ext,"nii",3)) { - std::strncpy(hname,filename,hname._width - 1); *iname = 0; - } - int *const iheader = (int*)header._data; - *iheader = 348; - std::strcpy(header._data + 4,"CImg"); - std::strcpy(header._data + 14," "); - ((short*)&(header[36]))[0] = 4096; - ((char*)&(header[38]))[0] = 114; - ((short*)&(header[40]))[0] = 4; - ((short*)&(header[40]))[1] = (short)_width; - ((short*)&(header[40]))[2] = (short)_height; - ((short*)&(header[40]))[3] = (short)_depth; - ((short*)&(header[40]))[4] = (short)_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._data,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"); - CImg header(257); - int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", - _width,_height,_depth,_spectrum); - if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", - voxel_size[0],voxel_size[1],voxel_size[2]); - err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); - std::memset(header._data + err,'\n',252 - err); - std::memcpy(header._data + 252,"##}\n",4); - cimg::fwrite(header._data,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 information). - **/ - 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) { CImg ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned int)==4) { CImg ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned short)==4) { CImg ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,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 .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; - CImg error_message(1024); - 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.data()); - - 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 the OpenCV library. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). - \param keep_open Tells if the video writer associated to the specified filename - must be kept open or not (to allow frames to be added in the same file afterwards). - **/ - const CImg& save_video(const char *const filename, const unsigned int fps=25, - const char *codec=0, const bool keep_open=false) const { - if (is_empty()) { CImgList().save_video(filename,fps,codec,keep_open); return *this; } - CImgList list; - get_split('z').move_to(list); - list.save_video(filename,fps,codec,keep_open); - return *this; - } - - //! Save volumetric image as a video, using ffmpeg external binary. - /** - \param filename Filename, as a C-string. - \param fps Video framerate. - \param codec Video codec, as a C-string. - \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 unsigned int fps=25, - const char *const codec=0, 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,fps,codec,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; } - - CImg command(1024), filename_tmp(256), body(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - save(filename_tmp); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gzip_path(), - CImg::string(filename_tmp)._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(filename_tmp); - 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 - CImg command(1024), filename_tmp(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(), - _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); -#ifdef cimg_use_png - save_png(filename_tmp); -#else - save_pnm(filename_tmp); -#endif - cimg_snprintf(command,command._width,"%s convert -quality %u \"%s\" \"%s\"", - cimg::graphicsmagick_path(),quality, - CImg::string(filename_tmp)._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(filename_tmp); - 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 - CImg command(1024), filename_tmp(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(), - cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); -#ifdef cimg_use_png - save_png(filename_tmp); -#else - save_pnm(filename_tmp); -#endif - cimg_snprintf(command,command._width,"%s -quality %u \"%s\" \"%s\"", - cimg::imagemagick_path(),quality, - CImg::string(filename_tmp)._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(filename_tmp); - 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; } - - CImg command(1024), filename_tmp(256), body(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - save_analyze(filename_tmp); - cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"", - cimg::medcon_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - std::remove(filename_tmp); - cimg::split_filename(filename_tmp,body); - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); - std::remove(filename_tmp); - - file = std::fopen(filename,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"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; - } - - //! Serialize a CImg instance into a raw CImg buffer. - /** - \param is_compressed tells if zlib compression must be used for serialization - (this requires 'cimg_use_zlib' been enabled). - **/ - CImg get_serialize(const bool is_compressed=false) const { - return CImgList(*this,true).get_serialize(is_compressed); - } - - // [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 with 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 with 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 shared state of 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,_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(); - } - - //! Return a reference to an empty list \const. - static const CImgList& const_empty() { - static const CImgList _empty; - return _empty; - } - - //@} - //------------------------------------------ - // - //! \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 \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 pixel value with Dirichlet boundary conditions for the 3 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 pixel value with Dirichlet boundary conditions for the 3 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 4 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 4 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 3 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 3 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 3 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 3 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 2 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 2 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 2 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 2 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 coordinate (\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 coordinate (\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 coordinate (\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 coordinate (\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) // Insert without re-allocation. - std::memmove(_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); - _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(CImg)*(_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=-1) { - 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=-1) 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 bool exit_on_anykey=false) const { - return _get_select(disp,0,feature_type,axis,align,exit_on_anykey,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 bool exit_on_anykey=false) const { - CImgDisplay disp; - return _get_select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); - } - - CImg _get_select(CImgDisplay &disp, const char *const title, const bool feature_type, - const char axis, const float align, const bool exit_on_anykey, - 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); - static 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]; - const CImg - img2d = src._depth>1?src.get_projections2d((src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2): - cimg::type::string()==cimg::type::string()?src.get_shared():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[indice0]._width, - _data[indice0]._height, - _data[indice0]._depth, - _data[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; - - CImg filename(32); - 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; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,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._data).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; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,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._data).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; }} - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - 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)) { - CImg filename_local(256); - load(cimg::load_network(filename,filename_local)); - std::remove(filename_local); - return *this; - } - - const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.'); - 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,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !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_video(filename); - else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); - else throw CImgIOException("CImgList<%s>::load()", - pixel_type()); - } catch (CImgIOException&) { - std::FILE *file = 0; - if (!is_stdin) try { - file = cimg::fopen(filename,"rb"); - } catch (CImgIOException&) { - cimg::exception_mode(omode); - throw CImgIOException(_cimglist_instance - "load(): Failed to open file '%s'.", - cimglist_instance, - filename); - } - - try { - if (!is_stdin) { - const char *const f_type = cimg::ftype(file,filename); - std::fclose(file); - if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); - else throw CImgIOException("CImgList<%s>::load()", - pixel_type()); - } else throw CImgIOException("CImgList<%s>::load()", - pixel_type()); - } catch (CImgIOException&) { - 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; \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ - raw.move_to(img); \ -} -#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 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \ - W = H = D = C = 0; csiz = 0; \ - if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&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]; \ - if (err==5) _cimgz_load_cimg_case(Tss) \ - else { \ - img.assign(W,H,D,C); \ - T *ptrd = img._data; \ - for (unsigned long to_read = img.size(); to_read; ) { \ - 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.size()); \ - const Tss *ptrs = raw._data; \ - for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ - to_read-=raw._width; \ - } \ - } \ - } \ - } \ - loaded = true; \ - } - - if (!filename && !file) - throw CImgArgumentException(_cimglist_instance - "load_cimg(): Specified filename is (null).", - cimglist_instance); - - const unsigned long cimg_iobuffer = 24*1024*1024; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - bool loaded = false, endian = cimg::endianness(); - CImg tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N = 0, W, H, D, C; - unsigned long csiz; - int i, err; - do { - j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; - } while (*tmp=='#' && i>=0); - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); - 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._data,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 (cimg_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(); - CImg tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N, W, H, D, C; - int i, err; - j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); - 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._data,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); - - CImg body(1024), filenamepar(1024), filenamerec(1024); - *body = *filenamepar = *filenamerec = 0; - const char *const ext = cimg::split_filename(filename,body); - if (!std::strcmp(ext,"par")) { - std::strncpy(filenamepar,filename,filenamepar._width - 1); - cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data); - } - if (!std::strcmp(ext,"PAR")) { - std::strncpy(filenamepar,filename,filenamepar._width - 1); - cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data); - } - if (!std::strcmp(ext,"rec")) { - std::strncpy(filenamerec,filename,filenamerec._width - 1); - cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data); - } - if (!std::strcmp(ext,"REC")) { - std::strncpy(filenamerec,filename,filenamerec._width - 1); - cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data); - } - std::FILE *file = cimg::fopen(filenamepar,"r"); - - // Parse header file - CImgList st_slices; - CImgList st_global; - CImg line(256); *line = 0; - int err; - do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } 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 OpenCV library. - /** - \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. - \note If step_frame==0, the current video stream is forced to be released (without any frames read). - **/ - CImgList& load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1) { -#ifndef cimg_use_opencv - if (first_frame || last_frame!=~0U || step_frame>1) - throw CImgArgumentException(_cimglist_instance - "load_video() : File '%s', arguments 'first_frame', 'last_frame' " - "and 'step_frame' can be only set when using OpenCV " - "(-Dcimg_use_opencv must be enabled).", - cimglist_instance,filename); - return load_ffmpeg_external(filename); -#else - static CvCapture *captures[32] = { 0 }; - static CImgList filenames(32); - static CImg positions(32,1,1,1,0); - static int last_used_index = -1; - - // Detect if a video capture already exists for the specified filename. - cimg::mutex(9); - int index = -1; - if (filename) { - if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { - index = last_used_index; - } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { - index = l; break; - } - } else index = last_used_index; - cimg::mutex(9,0); - - // Release stream if needed. - if (!step_frame || (index>=0 && positions[index]>first_frame)) { - if (index>=0) { - cimg::mutex(9); - cvReleaseCapture(&captures[index]); - captures[index] = 0; filenames[index].assign(); positions[index] = 0; - if (last_used_index==index) last_used_index = -1; - index = -1; - cimg::mutex(9,0); - } else - if (filename) - cimg::warn(_cimglist_instance - "load_video() : File '%s', no opened video stream associated with filename found.", - cimglist_instance,filename); - else - cimg::warn(_cimglist_instance - "load_video() : No opened video stream found.", - cimglist_instance,filename); - if (!step_frame) return *this; - } - - // Find empty slot for capturing video stream. - if (index<0) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load_video(): No already open video reader found. You must specify a " - "non-(null) filename argument for the first call.", - cimglist_instance); - else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } - if (index<0) - throw CImgIOException(_cimglist_instance - "load_video(): File '%s', no video reader slots available. " - "You have to release some of your previously opened videos.", - cimglist_instance,filename); - cimg::mutex(9); - captures[index] = cvCaptureFromFile(filename); - CImg::string(filename).move_to(filenames[index]); - positions[index] = 0; - cimg::mutex(9,0); - if (!captures[index]) { - filenames[index].assign(); - std::fclose(cimg::fopen(filename,"rb")); // Check file availability. - throw CImgIOException(_cimglist_instance - "load_video(): File '%s', unable to detect format of video file.", - cimglist_instance,filename); - } - } - - cimg::mutex(9); - const unsigned int nb_frames = (unsigned int)cimg::max(0.,cvGetCaptureProperty(captures[index], - CV_CAP_PROP_FRAME_COUNT)); - cimg::mutex(9,0); - assign(); - - // Skip frames if necessary. - unsigned int &pos = positions[index]; - while (pos frame(src->width,src->height,1,3); - const int step = (int)(src->widthStep - 3*src->width); - const unsigned char* ptrs = (unsigned char*)src->imageData; - T *ptr_r = frame.data(0,0,0,0), *ptr_g = frame.data(0,0,0,1), *ptr_b = frame.data(0,0,0,2); - if (step>0) cimg_forY(frame,y) { - cimg_forX(frame,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); } - ptrs+=step; - } else for (unsigned long siz = (unsigned long)src->width*src->height; siz; --siz) { - *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); - } - frame.move_to(*this); - ++pos; - - bool skip_failed = false; - for (unsigned int i = 1; i=nb_frames)) { // Close video stream when necessary. - cimg::mutex(9); - cvReleaseCapture(&captures[index]); - captures[index] = 0; - filenames[index].assign(); - positions[index] = 0; - index = -1; - cimg::mutex(9,0); - } - - cimg::mutex(9); - last_used_index = index; - cimg::mutex(9,0); - return *this; -#endif - } - - //! Load an image from a video file, using OpenCV library \newinstance. - static CImgList get_load_video(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_video(filename,first_frame,last_frame,step_frame); - } - - //! 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. - CImg command(1024), filename_tmp(256), filename_tmp2(256); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); - if ((file=std::fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); -#if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\" >/dev/null 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp2)._system_strescape().data()); -#else - cimg_snprintf(command,command._width,"\"%s -i \"%s\" \"%s\"\" >NUL 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp2)._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(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); - CImg img; - try { img.load_pnm(filename_tmp2); } - catch (CImgException&) { stop_flag = true; } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - } - 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) { - CImg command(1024), filename_tmp(256), filename_tmp2(256); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data); - else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); - if ((file=std::fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); -#if cimg_OS!=2 - if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\" >/dev/null 2>&1", - cimg::graphicsmagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\" >/dev/null 2>&1", - cimg::imagemagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); -#else - if (use_graphicsmagick) cimg_snprintf(command,command._width,"\"%s convert \"%s\" \"%s.png\"\" >NUL 2>&1", - cimg::graphicsmagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - else cimg_snprintf(command,command._width,"\"%s \"%s\" \"%s.png\"\" >NUL 2>&1", - cimg::imagemagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._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(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data); - CImg img; - try { img.load_png(filename_tmp2); } - catch (CImgException&) { } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - else { // Try to read animated gif. - unsigned int i = 0; - for (bool stop_flag = false; !stop_flag; ++i) { - if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i); - else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i); - CImg img; - try { img.load_png(filename_tmp2); } - catch (CImgException&) { stop_flag = true; } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - } - } - 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. - CImg command(1024), filename_tmp(256), body(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gunzip_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - if (!(file = std::fopen(filename_tmp,"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(filename_tmp); - std::remove(filename_tmp); - 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, - float *const voxel_size=0, - CImg *const description=0) { - 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,voxel_size,description); - 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, - float *const voxel_size=0, - CImg *const description=0) { - return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); - } - - //@} - //---------------------------------- - // - //! \name Data Output - //@{ - //---------------------------------- - - //! Print information about the list on the standard output. - /** - \param title Label set to the information 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?0U:msiz<8*1024*1024?1U:2U; - CImg _title(64); - if (!title) cimg_snprintf(_title,_title._width,"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._data,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 { - disp.display(*this,axis,align); - return *this; - } - - //! Display the current CImgList instance in a new display window. - /** - \param disp Display window. - \param display_info Tells if image information 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 exit_on_anykey=false) const { - bool is_exit = false; - return _display(disp,0,display_info,axis,align,XYZ,exit_on_anykey,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 information 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 bool exit_on_anykey=false) const { - CImgDisplay disp; - bool is_exit = false; - return _display(disp,title,display_info,axis,align,XYZ,exit_on_anykey,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 bool exit_on_anykey, 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,exit_on_anykey,!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,exit_on_anykey,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,exit_on_anykey, - 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,exit_on_anykey, - 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 bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); - const char *const ext = cimg::split_filename(filename); - CImg nfilename(1024); - const char *const fn = is_stdout?filename: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,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !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_video(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,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,stdout); } - } - 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,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !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 float fps=25, - const unsigned int nb_loops=0) { - CImg command(1024), filename_tmp(256), filename_tmp2(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_ext,filename_tmp._data); - if ((file=std::fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimglist_for(*this,l) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_ext,filename_tmp._data,l + 1); - CImg::string(filename_tmp2).move_to(filenames); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); - else _data[l].save(filename_tmp2); - } - -#if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -delay %u -loop %u", - cimg::imagemagick_path(),(unsigned int)cimg::max(0.0f,cimg::round(100/fps)),nb_loops); - CImg::string(command).move_to(filenames,0); - cimg_snprintf(command,command._width,"\"%s\" >/dev/null 2>&1", - CImg::string(filename)._system_strescape().data()); - CImg::string(command).move_to(filenames); -#else - cimg_snprintf(command,command._width,"\"%s -delay %u -loop %u", - cimg::imagemagick_path(),(unsigned int)cimg::max(0.0f,cimg::round(100/fps)),nb_loops); - CImg::string(command).move_to(filenames,0); - cimg_snprintf(command,command._width,"\"%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; - } - - 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(); - CImg tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N, W, H, D, C; - int i, err; - j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data); - 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._data); - } - 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 float *const voxel_size=0, const char *const description=0, - const bool use_bigtiff=true) 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,voxel_size,description,use_bigtiff); - else cimglist_for(*this,l) { - CImg nfilename(1024); - cimg::number_filename(filename,l,6,nfilename); - _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); - } -#else - typename CImg::uptrT siz = 0; - cimglist_for(*this,l) siz+=_data[l].size(); - const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images. - TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); - if (tif) { - for (unsigned int dir = 0, l = 0; l<_width; ++l) { - const CImg& img = (*this)[l]; - cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); - } - 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); - - CImg command(1024), filename_tmp(256), body(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=std::fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - - if (is_saveable(body)) { - save(filename_tmp); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gzip_path(), - CImg::string(filename_tmp)._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(filename_tmp); - } else { - CImg nfilename(1024); - cimglist_for(*this,l) { - cimg::number_filename(body,l,6,nfilename); - if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); - _data[l].save_gzip_external(nfilename); - } - } - return *this; - } - - //! Save image sequence, using the OpenCV library. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). - \param keep_open Tells if the video writer associated to the specified filename - must be kept open or not (to allow frames to be added in the same file afterwards). - **/ - const CImgList& save_video(const char *const filename, const unsigned int fps=25, - const char *codec=0, const bool keep_open=false) const { -#ifndef cimg_use_opencv - cimg::unused(codec,keep_open); - return save_ffmpeg_external(filename,fps); -#else - static CvVideoWriter *writers[32] = { 0 }; - static CImgList filenames(32); - static CImg sizes(32,2,1,1,0); - static int last_used_index = -1; - - // Detect if a video writer already exists for the specified filename. - cimg::mutex(9); - int index = -1; - if (filename) { - if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { - index = last_used_index; - } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { - index = l; break; - } - } else index = last_used_index; - cimg::mutex(9,0); - - // Find empty slot for capturing video stream. - if (index<0) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save_video(): No already open video writer found. You must specify a " - "non-(null) filename argument for the first call.", - cimglist_instance); - else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } - if (index<0) - throw CImgIOException(_cimglist_instance - "save_video(): File '%s', no video writer slots available. " - "You have to release some of your previously opened videos.", - cimglist_instance,filename); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_video(): Instance list is empty.", - cimglist_instance); - const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0; - if (!W || !H) - throw CImgInstanceException(_cimglist_instance - "save_video(): Frame [0] is an empty image.", - cimglist_instance); - -#define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x)) - const char - *const _codec = codec && *codec?codec:"mp4v", - codec0 = _cimg_docase(_codec[0]), - codec1 = _codec[0]?_cimg_docase(_codec[1]):0, - codec2 = _codec[1]?_cimg_docase(_codec[2]):0, - codec3 = _codec[2]?_cimg_docase(_codec[3]):0; - cimg::mutex(9); - writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3), - fps,cvSize(W,H)); - CImg::string(filename).move_to(filenames[index]); - sizes(index,0) = W; sizes(index,1) = H; - cimg::mutex(9,0); - if (!writers[index]) - throw CImgIOException(_cimglist_instance - "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.", - cimglist_instance,filename, - codec0,codec1,codec2,codec3); - } - - if (!is_empty()) { - const unsigned int W = sizes(index,0), H = sizes(index,1); - cimg::mutex(9); - IplImage *ipl = cvCreateImage(cvSize(W,H),8,3); - cimglist_for(*this,l) { - CImg &src = _data[l]; - if (src.is_empty()) - cimg::warn(_cimglist_instance - "save_video(): Skip empty frame %d for file '%s'.", - cimglist_instance,l,filename); - if (src._depth>1 || src._spectrum>3) - cimg::warn(_cimglist_instance - "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " - "Some image data may be ignored when writing frame into video file '%s'.", - cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); - if (src._width==W && src._height==H && src._spectrum==3) { - const T *ptr_r = src.data(0,0,0,0), *ptr_g = src.data(0,0,0,1), *ptr_b = src.data(0,0,0,2); - char *ptrd = ipl->imageData; - cimg_forXY(src,x,y) { - *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++); - } - } else { - CImg _src(src,false); - _src.channels(0,cimg::min(_src._spectrum - 1,2U)).resize(W,H); - _src.resize(W,H,1,3,_src._spectrum==1?1:0); - const unsigned char *ptr_r = _src.data(0,0,0,0), *ptr_g = _src.data(0,0,0,1), *ptr_b = _src.data(0,0,0,2); - char *ptrd = ipl->imageData; - cimg_forXY(_src,x,y) { - *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++); - } - } - cvWriteFrame(writers[index],ipl); - } - cvReleaseImage(&ipl); - cimg::mutex(9,0); - } - - cimg::mutex(9); - if (!keep_open) { - cvReleaseVideoWriter(&writers[index]); - writers[index] = 0; - filenames[index].assign(); - sizes(index,0) = sizes(index,1) = 0; - last_used_index = -1; - } else last_used_index = index; - cimg::mutex(9,0); - - return *this; -#endif - } - - //! Save image sequence, using the external tool 'ffmpeg'. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression. - \param bitrate Output bitrate - **/ - const CImgList& save_ffmpeg_external(const char *const filename, const unsigned int fps=25, - const char *const codec=0, 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"; - - CImg command(1024), filename_tmp(256), filename_tmp2(256); - 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(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); - if ((file=std::fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimglist_for(*this,l) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); - CImg::string(filename_tmp2).move_to(filenames); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2); - else _data[l].save_pnm(filename_tmp2); - } -#if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename_tmp)._system_strescape().data(), - _codec,bitrate,fps, - CImg::string(filename)._system_strescape().data()); -#else - cimg_snprintf(command,command._width,"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename_tmp)._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; - } - - //! Serialize a CImgList instance into a raw CImg buffer. - /** - \param is_compressed tells if zlib compression must be used for serialization - (this requires 'cimg_use_zlib' been enabled). - **/ - CImg get_serialize(const bool is_compressed=false) const { -#ifndef cimg_use_zlib - if (is_compressed) - cimg::warn(_cimglist_instance - "get_serialize(): Unable to compress data unless zlib is enabled, " - "storing them uncompressed.", - cimglist_instance); -#endif - CImgList stream; - CImg tmpstr(128); - const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; - if (std::strstr(ptype,"unsigned")==ptype) - cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); - else - cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); - CImg::string(tmpstr,false).move_to(stream); - cimglist_for(*this,l) { - const CImg& img = _data[l]; - cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); - CImg::string(tmpstr,false).move_to(stream); - 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 = compressBound(siz); - Bytef *const cbuf = new Bytef[csiz]; - if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) - cimg::warn(_cimglist_instance - "get_serialize(): Failed to save compressed data, saving them uncompressed.", - cimglist_instance); - else { - cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz); - CImg::string(tmpstr,false).move_to(stream); - CImg(cbuf,csiz).move_to(stream); - delete[] cbuf; - failed_to_compress = false; - } -#endif - } - if (failed_to_compress) { // Write in a non-compressed way. - CImg::string("\n",false).move_to(stream); - stream.insert(1); - stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); - } - } else CImg::string("\n",false).move_to(stream); - } - cimglist_apply(stream,unroll)('y'); - return stream>'y'; - } - - //! Unserialize a CImg serialized buffer into a CImgList list. - template - static CImgList get_unserialize(const CImg& buffer) { -#ifdef cimg_use_zlib -#define _cimgz_unserialize_case(Tss) { \ - Bytef *cbuf = (Bytef*)stream; \ - if (sizeof(t)!=1 || cimg::type::string()==cimg::type::string()) { \ - cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ - for (unsigned long i = 0; i raw(W,H,D,C); \ - unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \ - uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ - if (!is_bytef) delete[] cbuf; \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ - raw.move_to(img); \ - } -#else -#define _cimgz_unserialize_case(Tss) \ - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \ - "unless zlib is enabled.", \ - pixel_type()); -#endif - -#define _cimg_unserialize_case(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ - for (unsigned int l = 0; l::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ - "image #%u in serialized buffer.", \ - pixel_type(),W,H,D,C,l); \ - if (W*H*D*C>0) { \ - CImg &img = res._data[l]; \ - if (err==5) _cimgz_unserialize_case(Tss) \ - else { \ - if (sizeof(t)!=1) { \ - CImg raw(W*sizeof(Tss),H,D,C); \ - cimg_for(raw,p,unsigned char) *p = (unsigned char)*(stream++); \ - img.assign((Tss*)raw._data,W,H,D,C); \ - } else img.assign((Tss*)stream,W,H,D,C); \ - if (endian!=cimg::endianness()) cimg::invert_endianness(img._data,img.size()); \ - } \ - } \ - } \ - loaded = true; \ - } - - if (buffer.is_empty()) - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", - pixel_type()); - CImgList res; - const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); - bool loaded = false, endian = cimg::endianness(), is_bytef = false; - CImg tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N = 0, W, H, D, C; - unsigned long csiz; - int i, err; - cimg::unused(is_bytef); - do { - j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", - pixel_type()); - if (!cimg::strncasecmp("little",str_endian,6)) endian = false; - else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; - res.assign(N); - _cimg_unserialize_case("bool",bool); - _cimg_unserialize_case("unsigned_char",unsigned char); - _cimg_unserialize_case("uchar",unsigned char); - _cimg_unserialize_case("char",char); - _cimg_unserialize_case("unsigned_short",unsigned short); - _cimg_unserialize_case("ushort",unsigned short); - _cimg_unserialize_case("short",short); - _cimg_unserialize_case("unsigned_int",unsigned int); - _cimg_unserialize_case("uint",unsigned int); - _cimg_unserialize_case("int",int); - _cimg_unserialize_case("unsigned_long",unsigned long); - _cimg_unserialize_case("ulong",unsigned long); - _cimg_unserialize_case("long",long); - _cimg_unserialize_case("float",float); - _cimg_unserialize_case("double",double); - if (!loaded) - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " - "in serialized buffer.", - pixel_type(),str_pixeltype._data); - return res; - } - - //@} - //---------------------------------- - // - //! \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::const_empty(); - cimg::mutex(11); - - // Decompress nearest base font data if needed. - static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; - static 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<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U; - 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 = (int)(*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 = (unsigned int)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<33U?1U:font_height<53U?2U:font_height<103U?3U:4U; - 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 { - - //! 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, const bool reinit_path) { -#define _cimg_test_temporary_path(p) \ - if (!path_found) { \ - cimg_snprintf(s_path,s_path.width(),"%s",p); \ - cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ - if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ - } - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - CImg tmp(1024), filename_tmp(256); - std::FILE *file = 0; - cimg_snprintf(filename_tmp,filename_tmp._width,"%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,filename_tmp,tmp._width - 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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(MAX_PATH); - *s_path = 0; - // 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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"%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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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,s_path._width,"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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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,s_path._width,"%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,s_path._width,"%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,"C:\\XMedCon\\bin\\medcon.exe"); - 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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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 gunzip 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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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, const bool reinit_path) { - static CImg s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(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; - } - - // [internal] Sorting function, used by cimg::files(). - inline int _sort_files(const void* a, const void* b) { - const CImg &sa = *(CImg*)a, &sb = *(CImg*)b; - return std::strcmp(sa._data,sb._data); - } - - //! Return list of files/directories in specified directory. - /** - \param path Path to the directory. Set to 0 for current directory. - \param is_pattern Tell if specified path has a matching pattern in it. - \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }. - \param include_path Tell if \c path must be included in resulting filenames. - \return A list of filenames. - **/ - inline CImgList files(const char *const path, const bool is_pattern=false, - const unsigned int mode=2, const bool include_path=false) { - if (!path || !*path) return files("*",true,mode,include_path); - CImgList res; - - // If path is a valid folder name, ignore argument 'is_pattern'. - const bool _is_pattern = is_pattern && !cimg::is_directory(path); - bool is_root = false, is_current = false; - cimg::unused(is_root,is_current); - - // Clean format of input path. - CImg pattern, _path = CImg::string(path); -#if cimg_OS==2 - for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/'; -#endif - char *pd = _path; - for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } - *pd = 0; - unsigned int lp = (unsigned int)std::strlen(_path); - if (!_is_pattern && lp && _path[lp - 1]=='/') { - _path[lp - 1] = 0; --lp; -#if cimg_OS!=2 - is_root = !*_path; -#endif - } - - // Separate folder path and matching pattern. - if (_is_pattern) { - const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data()); - CImg::string(_path).move_to(pattern); - if (bpos) { - _path[bpos - 1] = 0; // End 'path' at last slash. -#if cimg_OS!=2 - is_root = !*_path; -#endif - } else { // No path to folder specified, assuming current folder. - is_current = true; *_path = 0; - } - lp = (unsigned int)std::strlen(_path); - } - - // Windows version. -#if cimg_OS==2 - if (!_is_pattern) { - pattern.assign(lp + 3); - std::memcpy(pattern,_path,lp); - pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; - } - WIN32_FIND_DATAA file_data; - const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); - if (dir==INVALID_HANDLE_VALUE) return CImgList::const_empty(); - do { - const char *const filename = file_data.cFileName; - if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = (unsigned int)std::strlen(filename); - const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; - if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { - if (include_path) { - CImg full_filename((lp?lp+1:0) + lf + 1); - if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } - std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); - full_filename.move_to(res); - } else CImg(filename,lf + 1).move_to(res); - } - } - } while (FindNextFileA(dir,&file_data)); - FindClose(dir); - - // Unix version (posix). -#else - DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); - if (!dir) return CImgList::const_empty(); - struct dirent *ent; - while ((ent=readdir(dir))!=0) { - const char *const filename = ent->d_name; - if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = (unsigned int)std::strlen(filename); - CImg full_filename(lp + lf + 2); - - if (!is_current) { - full_filename.assign(lp + lf + 2); - if (lp) std::memcpy(full_filename,_path,lp); - full_filename[lp] = '/'; - std::memcpy(full_filename._data + lp + 1,filename,lf + 1); - } else full_filename.assign(filename,lf + 1); - - struct stat st; - if (stat(full_filename,&st)==-1) continue; - const bool is_directory = (st.st_mode & S_IFDIR)!=0; - if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) { - if (include_path) { - if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) - full_filename.move_to(res); - } else { - if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) - CImg(filename,lf + 1).move_to(res); - } - } - } - } - closedir(dir); -#endif - - // Sort resulting list by lexicographic order. - if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg),_sort_files); - - return res; - } - - //! 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 *ftype(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException("cimg::ftype(): 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"; - const char *f_type = 0; - CImg header; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - header._load_raw(file,filename,512,1,1,1,false,false,0); - const unsigned char *const uheader = (unsigned char*)header._data; - if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF. - else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE. - else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE. - else if (!std::strncmp(header.data() + 128,"DICM",4)) f_type = _dcm; // DICOM. - else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // JPEG. - else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // BMP. - else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF. - (header[4]=='7' || header[4]=='9')) f_type = _gif; - else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // PNG. - 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; // TIFF. - else { // PNM or PFM. - CImgList _header = header.get_split(CImg::vector('\n'),0,false); - cimglist_for(_header,l) { - if (_header(l,0)=='#') continue; - if (_header[l]._height==2 && _header(l,0)=='P') { - const char c = _header(l,1); - if (c=='f' || c=='F') { f_type = _pfm; break; } - if (c>='1' && c<='9') { f_type = _pnm; break; } - } - f_type = 0; break; - } - } - } catch (CImgIOException&) { } - cimg::exception_mode(omode); - return f_type; - } - - //! 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. - \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. - \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. - \return Value of \c filename_local. - \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. - **/ - inline char *load_network(const char *const url, char *const filename_local, - const unsigned int timeout, const bool try_fallback, - const char *const referer) { - if (!url) - throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); - if (!filename_local) - throw CImgArgumentException("cimg::load_network(): Specified destination string is (null)."); - - const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext; - CImg ext = CImg::string(_ext); - std::FILE *file = 0; - *filename_local = 0; - if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0; - else cimg::strwindows_reserved(ext); - do { - cimg_snprintf(filename_local,256,"%s%c%s%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); - if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file); - } while (file); - -#ifdef cimg_use_curl - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - CURL *curl = 0; - CURLcode res; - curl = curl_easy_init(); - if (curl) { - file = cimg::fopen(filename_local,"wb"); - curl_easy_setopt(curl,CURLOPT_URL,url); - curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0); - curl_easy_setopt(curl,CURLOPT_WRITEDATA,file); - curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); - curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); - curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); - if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); - if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); - if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); - res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - std::fseek(file,0,SEEK_END); // Check if file size is 0. - const long siz = std::ftell(file); - cimg::fclose(file); - if (siz>0 && res==CURLE_OK) { - cimg::exception_mode(omode); - return filename_local; - } else std::remove(filename_local); - } - } catch (...) { } - cimg::exception_mode(omode); - if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); -#endif - - CImg command((unsigned int)std::strlen(url) + 64); - cimg::unused(try_fallback); - - // Try with 'curl' first. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,timeout,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),timeout,filename_local,url); - } else { - if (referer) - cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),filename_local,url); - } - cimg::system(command); - - if (!(file = std::fopen(filename_local,"rb"))) { - - // Try with 'wget' otherwise. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,timeout,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),timeout,filename_local,url); - } else { - if (referer) - cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),filename_local,url); - } - cimg::system(command); - - if (!(file = std::fopen(filename_local,"rb"))) - throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " - "'wget' or 'curl'.",url); - cimg::fclose(file); - - // Try gunzip it. - cimg_snprintf(command,command._width,"%s.gz",filename_local); - std::rename(filename_local,command); - cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"", - gunzip_path(),filename_local); - cimg::system(command); - file = std::fopen(filename_local,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"%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(): Failed to load URL '%s' with external commands " - "'wget' or 'curl'.",url); - cimg::fclose(file); - return filename_local; - } - - // 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; - } - - // Return a temporary string describing the size of a memory buffer. - inline const char *strbuffersize(const unsigned long size) { - static CImg res(256); - cimg::mutex(5); - if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",size,size>1?"s":""); - else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } - else if (size<1024*1024*1024LU) { - const float nsize = size/(1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); - } else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); } - cimg::mutex(5,0); - return res; - } - - //! 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 - static 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 -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif -// Local Variables: -// mode: c++ -// End: diff --git a/ucrop/src/main/jni/com_yalantis_ucrop_task_BitmapCropTask.h b/ucrop/src/main/jni/com_yalantis_ucrop_task_BitmapCropTask.h deleted file mode 100644 index cdab76e99..000000000 --- a/ucrop/src/main/jni/com_yalantis_ucrop_task_BitmapCropTask.h +++ /dev/null @@ -1,22 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class com_yalantis_ucrop_task_BitmapCropTask */ - -#ifndef _Included_com_yalantis_ucrop_task_BitmapCropTask -#define _Included_com_yalantis_ucrop_task_BitmapCropTask -#ifdef __cplusplus -extern "C" { -#endif - -/* -* Class: com_yalantis_ucrop_task_BitmapCropTask -* Method: cropCImg -* Signature: (Ljava/lang/String;Ljava/lang/String;IIIIF)Z -*/ -JNIEXPORT jboolean JNICALL Java_com_yalantis_ucrop_task_BitmapCropTask_cropCImg -(JNIEnv *, jobject, jstring, jstring, jint, jint, jint, jint, jfloat, jfloat, jint, jint, jint, jint); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/ucrop/src/main/jni/uCrop.cpp b/ucrop/src/main/jni/uCrop.cpp deleted file mode 100755 index 828dbd2b4..000000000 --- a/ucrop/src/main/jni/uCrop.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by Oleksii Shliama on 3/13/16. -// - -#include -#include -#include -#include -#include "com_yalantis_ucrop_task_BitmapCropTask.h" - -using namespace std; - -#define cimg_display 0 -#define cimg_use_jpeg -#define cimg_use_png -#define cimg_use_openmp - -#include "CImg.h" - -using namespace cimg_library; - -#define LOG_TAG "uCrop JNI" -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) - -#define SAVE_FORMAT_JPEG 0 -#define SAVE_FORMAT_PNG 1 - -JNIEXPORT jboolean JNICALL Java_com_yalantis_ucrop_task_BitmapCropTask_cropCImg - (JNIEnv *env, jobject obj, - jstring pathSource, jstring pathResult, - jint left, jint top, jint width, jint height, jfloat angle, jfloat resizeScale, - jint format, jint quality, - jint exifDegrees, jint exifTranslation) { - - LOGD("Crop image with CImg"); - - const char *file_source_path = env->GetStringUTFChars(pathSource, 0); - const char *file_result_path = env->GetStringUTFChars(pathResult, 0); - - try { - CImg img(file_source_path); - const int - x0 = left, y0 = top, - x1 = left + width - 1, y1 = top + height - 1; - - /* - LOGD("left %d\ntop: %d", left, top); - LOGD("width %d\nheight: %d", width, height); - LOGD("angle %f\nresizeScale: %f", angle, resizeScale); - LOGD("image size pre: %d x %d", img.width(), img.height()); - LOGD("exifDegrees: %d \nexifTranslation: %d", exifDegrees, exifTranslation); - */ - - // Handle exif. However it is slow, maybe calculate warp field according to exif rotation/translation. - if (exifDegrees != 0) { - img.rotate(exifDegrees); - } - if (exifTranslation != 1) { - img.mirror("x"); - } - - const int - size_x = img.width() * resizeScale, size_y = img.height() * resizeScale, - size_z = -100, size_c = -100, interpolation_type = 1; - - const unsigned int boundary_conditions = 0; - const float - centering_x = 0, centering_y = 0, centering_z = 0, centering_c = 0; - if (resizeScale != 1) { - img.resize(size_x, size_y, size_z, size_c, interpolation_type, boundary_conditions, centering_x, centering_y, centering_z, centering_c); - } - - // Create warp field. - CImg warp(cimg::abs(x1 - x0 + 1), cimg::abs(y1 - y0 + 1), 1, 2); - - const float - rad = angle * cimg::PI/180, - ca = std::cos(rad), sa = std::sin(rad), - ux = cimg::abs(img.width() * ca), uy = cimg::abs(img.width() * sa), - vx = cimg::abs(img.height() * sa), vy = cimg::abs(img.height() * ca), - w2 = 0.5f * img.width(), h2 = 0.5f * img.height(), - dw2 = 0.5f * (ux + vx), dh2 = 0.5f * (uy + vy); - - cimg_forXY(warp, x, y) { - const float - u = x + x0 - dw2, v = y + y0 - dh2; - - warp(x, y, 0) = w2 + u*ca + v*sa; - warp(x, y, 1) = h2 - u*sa + v*ca; - } - - img = img.get_warp(warp, 0, 1, 2); - - if (format == SAVE_FORMAT_JPEG) { - img.save_jpeg(file_result_path, quality); - } else if (format == SAVE_FORMAT_PNG) { - img.save_png(file_result_path, 0); - } else { - img.save(file_result_path); - } - - ~img; - env->ReleaseStringUTFChars(pathSource, file_source_path); - env->ReleaseStringUTFChars(pathResult, file_result_path); - - return true; - - } catch (CImgInstanceException e) { - env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), e.what()); - } catch (CImgIOException e) { - env->ThrowNew(env->FindClass("java/io/IOException"), e.what()); - } - - return false; -} \ No newline at end of file diff --git a/ucrop/src/main/jniLibs/arm64-v8a/libucrop.so b/ucrop/src/main/jniLibs/arm64-v8a/libucrop.so deleted file mode 100755 index e3911d6ccb1df8608e860e27538e986be682a477..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 886640 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