PTdecode/CImg-1.3.0/examples/gmic.cpp

changeset 5
1204ebf9340d
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/PTdecode/CImg-1.3.0/examples/gmic.cpp	Mon Aug 03 14:09:20 2009 +0100
     1.3 @@ -0,0 +1,4648 @@
     1.4 +/*
     1.5 + #
     1.6 + #  File        : gmic.cpp
     1.7 + #                ( C++ source file )
     1.8 + #
     1.9 + #  Description : GREYC's Magic Image Converter (library and executable)
    1.10 + #                ( http://gmic.sourceforge.net )
    1.11 + #                This file is a part of the CImg Library project.
    1.12 + #                ( http://cimg.sourceforge.net )
    1.13 + #
    1.14 + #  Copyright   : David Tschumperle
    1.15 + #                ( http://www.greyc.ensicaen.fr/~dtschump/ )
    1.16 + #
    1.17 + #  License     : CeCILL v2.0
    1.18 + #                ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
    1.19 + #
    1.20 + #  This software is governed by the CeCILL  license under French law and
    1.21 + #  abiding by the rules of distribution of free software.  You can  use,
    1.22 + #  modify and/ or redistribute the software under the terms of the CeCILL
    1.23 + #  license as circulated by CEA, CNRS and INRIA at the following URL
    1.24 + #  "http://www.cecill.info".
    1.25 + #
    1.26 + #  As a counterpart to the access to the source code and  rights to copy,
    1.27 + #  modify and redistribute granted by the license, users are provided only
    1.28 + #  with a limited warranty  and the software's author,  the holder of the
    1.29 + #  economic rights,  and the successive licensors  have only  limited
    1.30 + #  liability.
    1.31 + #
    1.32 + #  In this respect, the user's attention is drawn to the risks associated
    1.33 + #  with loading,  using,  modifying and/or developing or reproducing the
    1.34 + #  software by the user in light of its specific status of free software,
    1.35 + #  that may mean  that it is complicated to manipulate,  and  that  also
    1.36 + #  therefore means  that it is reserved for developers  and  experienced
    1.37 + #  professionals having in-depth computer knowledge. Users are therefore
    1.38 + #  encouraged to load and test the software's suitability as regards their
    1.39 + #  requirements in conditions enabling the security of their systems and/or
    1.40 + #  data to be ensured and,  more generally, to use and operate it in the
    1.41 + #  same conditions as regards security.
    1.42 + #
    1.43 + #  The fact that you are presently reading this means that you have had
    1.44 + #  knowledge of the CeCILL license and that you accept its terms.
    1.45 + #
    1.46 +*/
    1.47 +
    1.48 +// Add specific G'MIC methods to the CImg<T> class.
    1.49 +//-------------------------------------------------
    1.50 +#ifdef cimg_plugin
    1.51 +
    1.52 +template<typename t>
    1.53 +CImg<T> get_replace(const CImg<t>& img) const {
    1.54 +  return +img;
    1.55 +}
    1.56 +
    1.57 +template<typename t>
    1.58 +CImg<T>& replace(CImg<t>& img) {
    1.59 +  return img.transfer_to(*this);
    1.60 +}
    1.61 +
    1.62 +CImg<T>& gmic_set(const double value, const int x, const int y, const int z, const int v) {
    1.63 +  (*this).atXYZV(x,y,z,v,0) = (T)value;
    1.64 +  return *this;
    1.65 +}
    1.66 +
    1.67 +CImg<T> get_gmic_set(const double value, const int x, const int y, const int z, const int v) const {
    1.68 +  return (+*this).gmic_set(value,x,y,z,v);
    1.69 +}
    1.70 +
    1.71 +CImg<T> get_draw_point(const int x, const int y, const int z,
    1.72 +                       const CImg<T>& col, const float opacity) const {
    1.73 +  return (+*this).draw_point(x,y,z,col,opacity);
    1.74 +}
    1.75 +
    1.76 +CImg<T> get_draw_line(const int x0, const int y0, const int x1, const int y1,
    1.77 +                      const CImg<T>& col, const float opacity) const {
    1.78 +  return (+*this).draw_line(x0,y0,x1,y1,col,opacity);
    1.79 +}
    1.80 +
    1.81 +template<typename t>
    1.82 +CImg<T> get_draw_polygon(const CImg<t>& pts, const CImg<T>& col, const float opacity) const {
    1.83 +  return (+*this).draw_polygon(pts,col,opacity);
    1.84 +}
    1.85 +
    1.86 +CImg<T> get_draw_ellipse(const int x, const int y, const float r0, const float r1,
    1.87 +                         const float ru, const float rv, const CImg<T>& col, const float opacity) const {
    1.88 +  return (+*this).draw_ellipse(x,y,r0,r1,ru,rv,col,opacity);
    1.89 +}
    1.90 +
    1.91 +CImg<T> get_draw_text(const int x, const int y, const char *const text, const T *const col,
    1.92 +                      const int bg, const float opacity,const int siz) const {
    1.93 +  return (+*this).draw_text(x,y,text,col,bg,opacity,siz);
    1.94 +}
    1.95 +
    1.96 +CImg<T> get_draw_image(const int x, const int y, const int z,
    1.97 +                       const CImg<T>& sprite, const CImg<T>& mask, const float opacity) const {
    1.98 +  return (+*this).draw_image(x,y,z,sprite,mask,opacity);
    1.99 +}
   1.100 +
   1.101 +CImg<T> get_draw_image(const int x, const int y, const int z,
   1.102 +                       const CImg<T>& sprite, const float opacity) const {
   1.103 +  return (+*this).draw_image(x,y,z,sprite,opacity);
   1.104 +}
   1.105 +
   1.106 +CImg<T> get_draw_plasma(const float alpha, const float beta, const float opacity) const {
   1.107 +  return (+*this).draw_plasma(alpha,beta,opacity);
   1.108 +}
   1.109 +
   1.110 +CImg<T> get_draw_mandelbrot(const CImg<T>& color_palette, const float opacity,
   1.111 +                            const double z0r, const double z0i, const double z1r, const double z1i,
   1.112 +                            const unsigned int itermax, const bool normalized_iteration,
   1.113 +                            const bool julia_set, const double paramr, const double parami) const {
   1.114 +  return (+*this).draw_mandelbrot(color_palette,opacity,z0r,z0i,z1r,z1i,itermax,
   1.115 +                                  normalized_iteration,julia_set,paramr,parami);
   1.116 +}
   1.117 +
   1.118 +CImg<T> get_draw_fill(const int x, const int y, const int z,
   1.119 +                      const CImg<T>& col, const float opacity, const float tolerance) const {
   1.120 +  return (+*this).draw_fill(x,y,z,col,opacity,tolerance);
   1.121 +}
   1.122 +
   1.123 +bool is_CImg3d() const {
   1.124 +  const bool is_header = (width==1 && height>=8 && depth==1 && dim==1 &&
   1.125 +                          (*this)[0]=='C'+0.5f && (*this)[1]=='I'+0.5f &&
   1.126 +                          (*this)[2]=='m'+0.5f && (*this)[3]=='g'+0.5f &&
   1.127 +                          (*this)[4]=='3'+0.5f && (*this)[5]=='d'+0.5f);
   1.128 +  if (!is_header) return false;
   1.129 +  const int
   1.130 +    nbv = (int)(*this)[6],
   1.131 +    nbp = (int)(*this)[7];
   1.132 +  if (nbv<=0 || nbp<=0) return false;
   1.133 +  const T *ptrs = ptr() + 8 + 3*nbv, *const ptre = end();
   1.134 +  if (ptrs>=ptre) return false;
   1.135 +  for (int i = 0; i<nbp && ptrs<ptre; ++i) {
   1.136 +    const int N = (int)*(ptrs++);
   1.137 +    if (N<=0 || N>=8) return false;
   1.138 +    ptrs+=N;
   1.139 +  }
   1.140 +  ptrs+=4*nbp;
   1.141 +  if (ptrs>ptre) return false;
   1.142 +  return true;
   1.143 +}
   1.144 +
   1.145 +template<typename tp, typename tf, typename tc, typename to>
   1.146 +CImg<T> get_draw_object3d(const float x0, const float y0, const float z0,
   1.147 +                          const CImg<tp>& points, const CImgList<tf>& primitives,
   1.148 +                          const CImgList<tc>& colors, const CImg<to>& opacities,
   1.149 +                          const unsigned int render_type, const bool double_sided,
   1.150 +                          const float focale, const float lightx, const float lighty,
   1.151 +                          const float lightz, const float specular_light, const float specular_shine,
   1.152 +                          float *const zbuffer) const {
   1.153 +  return (+*this).draw_object3d(x0,y0,z0,points,primitives,colors,opacities,render_type,double_sided,focale,
   1.154 +                                lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
   1.155 +}
   1.156 +
   1.157 +template<typename tp, typename tc, typename to>
   1.158 +CImg<T>& object3dtoCImg3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
   1.159 +  if (is_empty() || !primitives) { primitives.assign(); colors.assign(); opacities.assign(); return *this; }
   1.160 +  const unsigned int primitives_size = primitives.size;
   1.161 +  CImgList<floatT> res;
   1.162 +  res.insert(CImg<floatT>("CImg3d",1,6,1,1,false)+=0.5f);
   1.163 +  res.insert(CImg<floatT>::vector((float)width,(float)primitives.size));
   1.164 +  res.insert(1); resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(res.last());
   1.165 +  cimglist_for(primitives,p) {
   1.166 +    res.insert(CImg<floatT>::vector((float)primitives[p].size())).insert(primitives[p]).last().unroll('y');
   1.167 +    primitives[p].assign();
   1.168 +  }
   1.169 +  primitives.assign();
   1.170 +  const unsigned int defined_colors = colors.size;
   1.171 +  cimglist_for(colors,c) { res.insert(colors[c]).last().resize(1,3,1,1,-1); colors[c].assign(); }
   1.172 +  colors.assign();
   1.173 +  if (defined_colors<primitives_size) res.insert(1).last().assign(1,3*(primitives_size-defined_colors),1,1,200);
   1.174 +  const unsigned int defined_opacities = opacities.size();
   1.175 +  res.insert(opacities).last().unroll('y');
   1.176 +  opacities.assign();
   1.177 +  if (defined_opacities<primitives.size) res.insert(1).last().assign(1,primitives_size-defined_opacities,1,1,1);
   1.178 +  return res.get_append('y').transfer_to(*this);
   1.179 +}
   1.180 +
   1.181 +template<typename tp, typename tc, typename to>
   1.182 +CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
   1.183 +  const T *ptrs = ptr() + 6;
   1.184 +  const unsigned int
   1.185 +    nbv = (unsigned int)*(ptrs++),
   1.186 +    nbp = (unsigned int)*(ptrs++);
   1.187 +  CImg<T> points(nbv,3);
   1.188 +  primitives.assign(nbp);
   1.189 +  colors.assign(nbp,1,3,1,1);
   1.190 +  opacities.assign(nbp);
   1.191 +  cimg_forX(points,x) { points(x,0) = (T)*(ptrs++); points(x,1) = (T)*(ptrs++); points(x,2) = (T)*(ptrs++); }
   1.192 +  cimglist_for(primitives,p) {
   1.193 +    const unsigned int N = (unsigned int)*(ptrs++);
   1.194 +    primitives[p].assign(ptrs,1,N,1,1,false);
   1.195 +    ptrs+=N;
   1.196 +  }
   1.197 +  cimglist_for(colors,c) { colors(c,0) = (tc)*(ptrs++); colors(c,1) = (tc)*(ptrs++); colors(c,2) = (tc)*(ptrs++); }
   1.198 +  opacities.assign(ptrs,1,nbp,1,1,false);
   1.199 +  return assign(points);
   1.200 +}
   1.201 +
   1.202 +CImg<T> get_appendCImg3d(const CImg<T>& img) const {
   1.203 +  CImg<T> res(1,img.size() + size() - 8);
   1.204 +  const T *ptrs = ptr() + 6, *ptrs0 = img.ptr() + 6;
   1.205 +  T *ptrd = res.ptr();
   1.206 +  *(ptrd++) = (T)('C' + 0.5f); *(ptrd++) = (T)('I' + 0.5f);
   1.207 +  *(ptrd++) = (T)('m' + 0.5f); *(ptrd++) = (T)('g' + 0.5f);
   1.208 +  *(ptrd++) = (T)('3' + 0.5f); *(ptrd++) = (T)('d' + 0.5f);
   1.209 +  const unsigned int
   1.210 +    nbv = (unsigned int)*(ptrs++),
   1.211 +    nbv0 = (unsigned int)*(ptrs0++),
   1.212 +    nbp = (unsigned int)*(ptrs++),
   1.213 +    nbp0 = (unsigned int)*(ptrs0++);
   1.214 +  *(ptrd++) = (T)(nbv + nbv0);
   1.215 +  *(ptrd++) = (T)(nbp + nbp0);
   1.216 +  std::memcpy(ptrd,ptrs,sizeof(T)*nbv*3);
   1.217 +  ptrd+=3*nbv; ptrs+=3*nbv;
   1.218 +  std::memcpy(ptrd,ptrs0,sizeof(T)*nbv0*3);
   1.219 +  ptrd+=3*nbv0; ptrs0+=3*nbv0;
   1.220 +  for (unsigned int i = 0; i<nbp; ++i) {
   1.221 +    const unsigned int N = (unsigned int)*(ptrs++);
   1.222 +    *(ptrd++) = (T)N;
   1.223 +    std::memcpy(ptrd,ptrs,sizeof(T)*N);
   1.224 +    ptrd+=N; ptrs+=N;
   1.225 +  }
   1.226 +  for (unsigned int i = 0; i<nbp0; ++i) {
   1.227 +    const unsigned int N = (unsigned int)*(ptrs0++);
   1.228 +    *(ptrd++) = (T)N;
   1.229 +    for (unsigned int j = 0; j<N; ++j) *(ptrd++) = (T)(*(ptrs0++) + nbv);
   1.230 +  }
   1.231 +  std::memcpy(ptrd,ptrs,sizeof(T)*nbp*3);
   1.232 +  ptrd+=3*nbp; ptrs+=3*nbp;
   1.233 +  std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0*3);
   1.234 +  ptrd+=3*nbp0; ptrs0+=3*nbp0;
   1.235 +  std::memcpy(ptrd,ptrs,sizeof(T)*nbp);
   1.236 +  ptrd+=nbp;
   1.237 +  std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0);
   1.238 +  return res;
   1.239 +}
   1.240 +
   1.241 +CImg<T>& appendCImg3d(const CImg<T>& img) {
   1.242 +  return get_appendCImg3d(img).transfer_to(*this);
   1.243 +}
   1.244 +
   1.245 +CImg<T>& centerCImg3d() {
   1.246 +  const unsigned int nbv = (unsigned int)(*this)[6];
   1.247 +  const T *ptrs = ptr() + 8;
   1.248 +  float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
   1.249 +  for (unsigned int i = 0; i<nbv; ++i) {
   1.250 +    const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   1.251 +    if (x<xm) xm = x; if (x>xM) xM = x;
   1.252 +    if (y<ym) ym = y; if (y>yM) yM = y;
   1.253 +    if (z<zm) zm = z; if (z>zM) zM = z;
   1.254 +  }
   1.255 +  const float xc = (xm + xM)/2, yc = (ym + yM)/2, zc = (zm + zM)/2;
   1.256 +  T *ptrd = ptr() + 8;
   1.257 +  for (unsigned int i = 0; i<nbv; ++i) { *(ptrd++)-=(T)xc; *(ptrd++)-=(T)yc; *(ptrd++)-=(T)zc; }
   1.258 +  return *this;
   1.259 +}
   1.260 +
   1.261 +CImg<T> get_centerCImg3d() const {
   1.262 +  return (+*this).centerCImg3d();
   1.263 +}
   1.264 +
   1.265 +CImg<T>& normalizeCImg3d() {
   1.266 +  const unsigned int nbv = (unsigned int)(*this)[6];
   1.267 +  const T *ptrs = ptr() + 8;
   1.268 +  float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
   1.269 +  for (unsigned int i = 0; i<nbv; ++i) {
   1.270 +    const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   1.271 +    if (x<xm) xm = x; if (x>xM) xM = x;
   1.272 +    if (y<ym) ym = y; if (y>yM) yM = y;
   1.273 +    if (z<zm) zm = z; if (z>zM) zM = z;
   1.274 +  }
   1.275 +  const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
   1.276 +  if (delta>0) {
   1.277 +    T *ptrd = ptr() + 8;
   1.278 +    for (unsigned int i = 0; i<3*nbv; ++i) *(ptrd++)/=(T)delta;
   1.279 +  }
   1.280 +  return *this;
   1.281 +}
   1.282 +
   1.283 +CImg<T> get_normalizeCImg3d() const {
   1.284 +  return (+*this).normalizeCImg3d();
   1.285 +}
   1.286 +
   1.287 +template<typename t>
   1.288 +CImg<T>& rotateCImg3d(const CImg<t>& rot) {
   1.289 +  const unsigned int nbv = (unsigned int)(*this)[6];
   1.290 +  const T *ptrs = ptr() + 8;
   1.291 +  const float
   1.292 +    a = (float)rot(0,0), b = (float)rot(1,0), c = (float)rot(2,0),
   1.293 +    d = (float)rot(0,1), e = (float)rot(1,1), f = (float)rot(2,1),
   1.294 +    g = (float)rot(0,2), h = (float)rot(1,2), i = (float)rot(2,2);
   1.295 +  T *ptrd = ptr() + 8;
   1.296 +  for (unsigned int j = 0; j<nbv; ++j) {
   1.297 +    const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   1.298 +    *(ptrd++) = (T)(a*x + b*y + c*z);
   1.299 +    *(ptrd++) = (T)(d*x + e*y + f*z);
   1.300 +    *(ptrd++) = (T)(g*x + h*y + i*z);
   1.301 +  }
   1.302 +  return *this;
   1.303 +}
   1.304 +
   1.305 +template<typename t>
   1.306 +CImg<T> get_rotateCImg3d(const CImg<t>& rot) const {
   1.307 +  return (+*this).rotateCImg3d(rot);
   1.308 +}
   1.309 +
   1.310 +CImg<T>& translateCImg3d(const float tx, const float ty, const float tz) {
   1.311 +  const unsigned int nbv = (unsigned int)(*this)[6];
   1.312 +  T *ptrd = ptr() + 8;
   1.313 +  for (unsigned int j = 0; j<nbv; ++j) { *(ptrd++) += (T)tx; *(ptrd++) += (T)ty; *(ptrd++) += (T)tz; }
   1.314 +  return *this;
   1.315 +}
   1.316 +
   1.317 +CImg<T> get_translateCImg3d(const float tx, const float ty, const float tz) const {
   1.318 +  return (+*this).translateCImg3d(tx,ty,tz);
   1.319 +}
   1.320 +
   1.321 +CImg<T>& coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) {
   1.322 +  T *ptrd = ptr() + 6;
   1.323 +  const unsigned int
   1.324 +    nbv = (unsigned int)*(ptrd++),
   1.325 +    nbp = (unsigned int)*(ptrd++);
   1.326 +  ptrd+=3*nbv;
   1.327 +  for (unsigned int i = 0; i<nbp; ++i) { const unsigned int N = (unsigned int)*(ptrd++); ptrd+=N; }
   1.328 +  if (set_RGB) for (unsigned int c = 0; c<nbp; ++c) { *(ptrd++) = (T)R; *(ptrd++) = (T)G; *(ptrd++) = (T)B; } else ptrd+=3*nbp;
   1.329 +  if (set_opacity) for (unsigned int o = 0; o<nbp; ++o) *(ptrd++) = (T)opacity;
   1.330 +  return *this;
   1.331 +}
   1.332 +
   1.333 +CImg<T> get_coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) const {
   1.334 +  return (+*this).coloropacityCImg3d(R,G,B,opacity,set_RGB,set_opacity);
   1.335 +}
   1.336 +
   1.337 +#else  // eq. to #ifndef cimg_plugin
   1.338 +
   1.339 +#define cimg_debug 1
   1.340 +#ifndef cimg_gmic_cpp
   1.341 +#define cimg_gmic_cpp "examples/gmic.cpp"
   1.342 +#define cimg_cimg_h "../CImg.h"
   1.343 +#endif
   1.344 +#define cimg_stdout stdout
   1.345 +#define cimg_plugin cimg_gmic_cpp
   1.346 +#include cimg_cimg_h
   1.347 +#include "gmic.h"
   1.348 +using namespace cimg_library;
   1.349 +
   1.350 +// The lines below are necessary when using a non-standard compiler such as visualcpp6.
   1.351 +#ifdef cimg_use_visualcpp6
   1.352 +#define std
   1.353 +#endif
   1.354 +#ifdef min
   1.355 +#undef min
   1.356 +#undef max
   1.357 +#endif
   1.358 +
   1.359 +#if !defined(gmic_main) || !defined(gmic_separate_compilation)
   1.360 +
   1.361 +// Define some useful macros.
   1.362 +//---------------------------
   1.363 +
   1.364 +// Code for validity checking of indices.
   1.365 +#define gmic_inds indices2string(indices,true)
   1.366 +#define gmic_check_indice(ind,funcname) { \
   1.367 +  const int indo = (int)ind; \
   1.368 +  if (ind<0) ind+=images.size; \
   1.369 +  if (ind<0 || ind>=(int)images.size) { \
   1.370 +    if (images.size) error(funcname " : Invalid indice '[%d]' (valid indice range is -%u...%u).",gmic_inds,indo,images.size,images.size-1); \
   1.371 +    else error(funcname " : Invalid indice '[%d]' (image list is empty).",gmic_inds,indo); \
   1.372 +  } \
   1.373 +}
   1.374 +
   1.375 +// Code for having 'get' or 'non-get' versions of G'MIC commands.
   1.376 +#define gmic_apply(instance,function) { \
   1.377 +  if (get_version) { \
   1.378 +    unsigned int posi = 0; \
   1.379 +    if (images.contains(instance,posi)) filenames.insert(filenames[posi]); \
   1.380 +    else filenames.insert(CImg<char>("(gmic)",7,1,1,1,false)); \
   1.381 +    CImg<T> res = instance.get_##function; \
   1.382 +    images.insert(1); res.transfer_to(images.last()); \
   1.383 +  } else instance.function; \
   1.384 +}
   1.385 +
   1.386 +// Code for simple commands that has no parameters and act on images.
   1.387 +#define gmic_simple_item(option,function,description) \
   1.388 +  if (!cimg::strcmp(option,item0)) { \
   1.389 +    print(description,gmic_inds); cimg_foroff(indices,l) gmic_apply(images[indices[l]],function()); \
   1.390 +    continue; \
   1.391 +}
   1.392 +
   1.393 +// Code for the type cast command.
   1.394 +#define gmic_cast(pixel_type,st_type) \
   1.395 +  if (!cimg::strcmp(#pixel_type,argument)) { \
   1.396 +    print("Set pixel type to '%s'.",#pixel_type); ++position; \
   1.397 +    if (!cimg::strcmp(st_type,cimg::type<T>::string())) continue; \
   1.398 +    CImgList<pixel_type> casted_images; \
   1.399 +    while (images) { casted_images.insert(images[0]); images.remove(0); } \
   1.400 +    return parse_##pixel_type(casted_images); \
   1.401 +}
   1.402 +
   1.403 +// Code for G'MIC arithmetic commands.
   1.404 +#define gmic_arithmetic_item(option1,option2,\
   1.405 +                             function1,description1,arg1_1,arg1_2,value_type1, \
   1.406 +                             function2,description2_1,description2_2,arg2_1,arg2_2,description3) \
   1.407 + if (!cimg::strcmp(option1,item0) || !cimg::strcmp(option2,item0)) { \
   1.408 +   double value = 0; char inds[4096] = { 0 }, sep = 0, end = 0; \
   1.409 +    if (std::sscanf(argument,"%lf%c",&value,&end)==1) { \
   1.410 +      print(description1 ".",arg1_1,arg1_2); \
   1.411 +      cimg_foroff(indices,l) \
   1.412 +       if (get_version) { \
   1.413 +         images.insert(images[indices[l]]); images.last().function1((value_type1)value); \
   1.414 +         filenames.insert(filenames[indices[l]]); } \
   1.415 +       else images[indices[l]].function1((value_type1)value); \
   1.416 +      ++position; \
   1.417 +    } else if (std::sscanf(argument,"[%4095[0-9.eE%+-]%c%c",inds,&sep,&end)==2 && sep==']') { \
   1.418 +      const CImg<unsigned int> ind = indices2cimg(inds,images.size,option1); \
   1.419 +      if (ind.size()!=1) error(description2_1 " : Argument '[%s]' should contain one indice.",gmic_inds,inds); \
   1.420 +      print(description2_2 ".",arg2_1,arg2_2); \
   1.421 +      const CImg<T> img0 = images[ind[0]]; \
   1.422 +      cimg_foroff(indices,l) \
   1.423 +       if (get_version) { \
   1.424 +         images.insert(images[indices[l]]); images.last().function2(img0); \
   1.425 +         filenames.insert(filenames[indices[l]]); } \
   1.426 +       else images[indices[l]].function2(img0); \
   1.427 +      ++position; \
   1.428 +    } else { \
   1.429 +      print(description3 ".",gmic_inds); \
   1.430 +      if (images && indices) { \
   1.431 +        for (unsigned int siz = indices.size(), ind0 = indices[0], off = 0, l = 1; l<siz; ++l) { \
   1.432 +          const unsigned int ind = indices[l] - off; \
   1.433 +          images[ind0].function2(images[ind]); \
   1.434 +          images.remove(ind); filenames.remove(ind); \
   1.435 +          ++off; \
   1.436 +        }}} continue; \
   1.437 +}
   1.438 +
   1.439 +// Constructors.
   1.440 +//--------------
   1.441 +#if defined(gmic_float) || !defined(gmic_separate_compilation)
   1.442 +
   1.443 +#include "gmic_def.h"
   1.444 +
   1.445 +gmic_exception::gmic_exception() {
   1.446 +  message[0] = '\0';
   1.447 +}
   1.448 +
   1.449 +gmic_exception::gmic_exception(const char *format, ...) {
   1.450 +  std::va_list ap;
   1.451 +  va_start(ap,format);
   1.452 +  std::vsprintf(message,format,ap);
   1.453 +  va_end(ap);
   1.454 +}
   1.455 +
   1.456 +gmic_exception::gmic_exception(const char *format, std::va_list ap) {
   1.457 +  std::vsprintf(message,format,ap);
   1.458 +}
   1.459 +
   1.460 +gmic::gmic() {
   1.461 +  assign(0);
   1.462 +}
   1.463 +
   1.464 +// Set default values of G'MIC parameters and macros.
   1.465 +//----------------------------------------------------
   1.466 +gmic& gmic::assign(const unsigned int size, const char *const custom_macros, const bool add_macros_start) {
   1.467 +  filenames.assign(size,CImg<char>("(gmic)",7,1,1,1,false));
   1.468 +  position = 0;
   1.469 +  verbosity_level = 0;
   1.470 +  is_released = true;
   1.471 +  is_debug = false;
   1.472 +  is_begin = true;
   1.473 +  background3d[0] = 120;
   1.474 +  background3d[1] = 120;
   1.475 +  background3d[2] = 140;
   1.476 +  render3d = 4;
   1.477 +  renderd3d = -1;
   1.478 +  is_oriented3d = false;
   1.479 +  focale3d = 500;
   1.480 +  light3d_x = 0;
   1.481 +  light3d_y = 0;
   1.482 +  light3d_z = -5000;
   1.483 +  specular_light3d = 0.15f;
   1.484 +  specular_shine3d = 0.8f;
   1.485 +  is_fullpath = false;
   1.486 +  add_macros(data_def,sizeof(data_def)-1,true);
   1.487 +  add_macros(custom_macros,cimg::strlen(custom_macros)-1,add_macros_start);
   1.488 +  return *this;
   1.489 +}
   1.490 +
   1.491 +// Error procedure.
   1.492 +//-----------------
   1.493 +const gmic& gmic::error(const char *format, ...) const {
   1.494 +  va_list ap;
   1.495 +  va_start(ap,format);
   1.496 +  char message[1024] = { 0 };
   1.497 +  std::vsprintf(message,format,ap);
   1.498 +  va_end(ap);
   1.499 +  if (verbosity_level>=0) {
   1.500 +    std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Error ** %s",filenames.size,message);
   1.501 +    std::fprintf(cimg_stdout,"\n<gmic-#%u> Abort G'MIC instance.\n",filenames.size);
   1.502 +    std::fflush(cimg_stdout);
   1.503 +  }
   1.504 +  throw gmic_exception(message);
   1.505 +  return *this;
   1.506 +}
   1.507 +
   1.508 +// Warning procedure.
   1.509 +//-------------------
   1.510 +const gmic& gmic::warning(const char *format, ...) const {
   1.511 +  va_list ap;
   1.512 +  va_start(ap,format);
   1.513 +  if (verbosity_level>=0) {
   1.514 +    std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Warning ** ",filenames.size);
   1.515 +    std::vfprintf(cimg_stdout,format,ap);
   1.516 +    std::fflush(cimg_stdout);
   1.517 +  }
   1.518 +  va_end(ap);
   1.519 +  return *this;
   1.520 +}
   1.521 +
   1.522 +// Print debug messages.
   1.523 +//----------------------
   1.524 +const gmic& gmic::debug(const char *format, ...) const {
   1.525 +  const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' };
   1.526 +  const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' };
   1.527 +  const char t_bold[] = { 0x1b,'[','1','m','\0' };
   1.528 +  if (is_debug) {
   1.529 +    va_list ap;
   1.530 +    va_start(ap,format);
   1.531 +    std::fprintf(cimg_stdout,"\n%s%s<gmic-debug-#%u>%s ",t_bold,t_red,filenames.size,t_normal);
   1.532 +    std::vfprintf(cimg_stdout,format,ap);
   1.533 +    va_end(ap);
   1.534 +    std::fflush(cimg_stdout);
   1.535 +  }
   1.536 +  return *this;
   1.537 +}
   1.538 +
   1.539 +// Print status messages.
   1.540 +//-----------------------
   1.541 +const gmic& gmic::print(const char *format, ...) const {
   1.542 +  va_list ap;
   1.543 +  va_start(ap,format);
   1.544 +  if (verbosity_level>=0) {
   1.545 +    std::fprintf(cimg_stdout,"\n<gmic-#%u> ",filenames.size);
   1.546 +    std::vfprintf(cimg_stdout,format,ap);
   1.547 +    std::fflush(cimg_stdout);
   1.548 +  }
   1.549 +  va_end(ap);
   1.550 +  return *this;
   1.551 +}
   1.552 +
   1.553 +// Add macros from a char* buffer.
   1.554 +//---------------------------------
   1.555 +gmic& gmic::add_macros(const char *const data_macros, const unsigned int data_size, const bool add_macros_at_start) {
   1.556 +  if (!data_macros || !data_size) return *this;
   1.557 +  char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
   1.558 +  const char *data = data_macros, *const data_end = data_macros + data_size;
   1.559 +  while (data<data_end) {
   1.560 +    if (*data=='\n') ++data;
   1.561 +    else {
   1.562 +      if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line) + 1;
   1.563 +      if (line[0]!='#') { // Useful line (not a comment)
   1.564 +        mac[0] = com[0] = 0;
   1.565 +        if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
   1.566 +            std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
   1.567 +          macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
   1.568 +          commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
   1.569 +        } else { // Possible continuation of a previous macro definition.
   1.570 +          if (!macros) error("Fatal error : Invalid G'MIC macros data.");
   1.571 +          CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
   1.572 +          last[last.size()-1] = ' ';
   1.573 +          last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
   1.574 +        }
   1.575 +      }
   1.576 +    }
   1.577 +  }
   1.578 +  return *this;
   1.579 +}
   1.580 +
   1.581 +// Add macros from a macro file.
   1.582 +//------------------------------
   1.583 +gmic& gmic::add_macros(std::FILE *const file, const bool add_macros_at_start) {
   1.584 +  if (!file) return *this;
   1.585 +  char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
   1.586 +  int err = 0;
   1.587 +  while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
   1.588 +    if (err) { // Non empty-line
   1.589 +      mac[0] = com[0] = 0;
   1.590 +      if (line[0]!='#') { // Useful line (not a comment).
   1.591 +        if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
   1.592 +            std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
   1.593 +          macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
   1.594 +          commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
   1.595 +        } else { // Possible continuation of a previous macro definition.
   1.596 +          if (!macros) error("Fatal error : Invalid G'MIC macros data.");
   1.597 +          CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
   1.598 +          last[last.size()-1] = ' ';
   1.599 +          last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
   1.600 +        }
   1.601 +      }
   1.602 +    }
   1.603 +  }
   1.604 +  return *this;
   1.605 +}
   1.606 +
   1.607 +// Return indices of the images from a string.
   1.608 +//--------------------------------------------
   1.609 +CImg<unsigned int> gmic::indices2cimg(const char *const string, const unsigned int indice_max,
   1.610 +                                      const char *const command) const {
   1.611 +  if (!cimg::strlen(string)) return CImg<unsigned int>();
   1.612 +  CImgList<unsigned int> inds;
   1.613 +  const char *it = string;
   1.614 +  for (bool stopflag = false; !stopflag; ) {
   1.615 +    char sep = 0, end = 0, item0[4096] = { 0 }, item1[4096] = { 0 };
   1.616 +    float ind0 = 0, ind1 = 0, step = 1;
   1.617 +    if (std::sscanf(it,"%4095[^,]%c",item0,&end)!=2) stopflag = true;
   1.618 +    else it += 1 + cimg::strlen(item0);
   1.619 +    const int err = std::sscanf(item0,"%4095[^:]%c%f%c",item1,&sep,&step,&end);
   1.620 +    if (err!=1 && err!=3) error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
   1.621 +    if (std::sscanf(item1,"%f%%-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%') {
   1.622 +      ind0 = (float)cimg::round(ind0*indice_max/100,1);
   1.623 +      ind1 = (float)cimg::round(ind1*indice_max/100,1);
   1.624 +    } else if (std::sscanf(item1,"%f%%-%f%c",&ind0,&ind1,&end)==2)
   1.625 +      ind0 = (float)cimg::round(ind0*indice_max/100,1);
   1.626 +    else if (std::sscanf(item1,"%f-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%')
   1.627 +      ind1 = (float)cimg::round(ind1*indice_max/100,1);
   1.628 +    else if (std::sscanf(item1,"%f-%f%c",&ind0,&ind1,&end)==2) { }
   1.629 +    else if (std::sscanf(item1,"%f%c%c",&ind0,&sep,&end)==2 && sep=='%')
   1.630 +      ind1 = (ind0 = (float)cimg::round(ind0*indice_max/100,1));
   1.631 +    else if (std::sscanf(item1,"%f%c",&ind0,&end)==1)
   1.632 +      ind1 = ind0;
   1.633 +    else error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
   1.634 +    if (ind0<0) ind0+=indice_max;
   1.635 +    if (ind1<0) ind1+=indice_max;
   1.636 +    if (ind0<0 || ind0>=indice_max || ind1<0 || ind1>=indice_max || step<=0) {
   1.637 +      if (indice_max) error("Command '%s' : Invalid indice(s) '[%s]' (valid indice range is -%u...%u).",
   1.638 +                            command,string,indice_max,indice_max-1);
   1.639 +      else error("Command '%s' : Invalid indice(s) '[%s]' (image list is empty).",
   1.640 +                 command,string);
   1.641 +    }
   1.642 +    if (ind0>ind1) cimg::swap(ind0,ind1);
   1.643 +    const unsigned int
   1.644 +      iind0 = (unsigned int)ind0,
   1.645 +      _ind1 = (unsigned int)ind1,
   1.646 +      iind1 = (unsigned int)(_ind1 - cimg::mod((float)_ind1,step));
   1.647 +    if (iind0==iind1) inds.insert(CImg<unsigned int>::vector(iind0));
   1.648 +    else inds.insert(CImg<unsigned int>::sequence((unsigned int)(1+(iind1-iind0)/step),
   1.649 +                                                  (unsigned int)iind0,
   1.650 +                                                  (unsigned int)iind1).get_split('y'));
   1.651 +  }
   1.652 +  inds = inds.get_append('y').sort().get_split('y');
   1.653 +  cimglist_for(inds,l) if (l!=inds.size-1 && inds(l,0)==inds(l+1,0)) inds.remove(l--);
   1.654 +  if (is_debug) {
   1.655 +    debug("Indices : ");
   1.656 +    inds.get_append('y').print(); // List indices if debug mode is activated.
   1.657 +  }
   1.658 +  return inds.get_append('y').sort();
   1.659 +}
   1.660 +
   1.661 +// Return stringified version of indices or filenames.
   1.662 +//----------------------------------------------------
   1.663 +char* gmic::indices2string(const CImg<unsigned int>& indices, const bool display_indices) const {
   1.664 +  static char res0[4096] = { 0 }, res1[4096] = { 0 };
   1.665 +  const unsigned int siz = indices.size();
   1.666 +  if (display_indices) {
   1.667 +    switch (siz) {
   1.668 +    case 0: std::sprintf(res0," []"); break;
   1.669 +    case 1: std::sprintf(res0," [%u]",indices[0]); break;
   1.670 +    case 2: std::sprintf(res0,"s [%u,%u]",indices[0],indices[1]); break;
   1.671 +    case 3: std::sprintf(res0,"s [%u,%u,%u]",indices[0],indices[1],indices[2]); break;
   1.672 +    case 4: std::sprintf(res0,"s [%u,%u,%u,%u]",indices[0],indices[1],indices[2],indices[3]); break;
   1.673 +    default: std::sprintf(res0,"s [%u,...,%u]",indices[0],indices[siz-1]);
   1.674 +    }
   1.675 +    return res0;
   1.676 +  }
   1.677 +  switch (siz) {
   1.678 +  case 0: std::sprintf(res1," "); break;
   1.679 +  case 1: std::sprintf(res1,"%s",filenames[indices[0]].ptr()); break;
   1.680 +  case 2: std::sprintf(res1,"%s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr()); break;
   1.681 +  case 3: std::sprintf(res1,"%s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
   1.682 +                       filenames[indices[2]].ptr()); break;
   1.683 +  case 4: std::sprintf(res1,"%s, %s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
   1.684 +                       filenames[indices[2]].ptr(), filenames[indices[3]].ptr()); break;
   1.685 +  default: std::sprintf(res1,"%s, ..., %s",filenames[indices[0]].ptr(),filenames[indices[siz-1]].ptr());
   1.686 +  }
   1.687 +  return res1;
   1.688 +}
   1.689 +#endif // #if defined(gmic_float) || !defined(gmic_separate_compilation)
   1.690 +
   1.691 +// Template constructors.
   1.692 +//-----------------------
   1.693 +template<typename T>
   1.694 +gmic::gmic(const int argc, const char *const *const argv, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
   1.695 +  assign(images.size,custom_macros,add_macros_at_start);
   1.696 +  for (int pos = 1; pos<argc; ++pos)
   1.697 +    command_line.insert(CImg<char>(argv[pos],cimg::strlen(argv[pos])+1,1,1,1,false));
   1.698 +  is_released = false;
   1.699 +  parse(images);
   1.700 +}
   1.701 +
   1.702 +template<typename T>
   1.703 +gmic::gmic(const char *const command, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
   1.704 +  assign(images.size,custom_macros,add_macros_at_start);
   1.705 +  char item[4096] = { 0 };
   1.706 +  for (const char *ncommand = command; *ncommand; ) {
   1.707 +    if (std::sscanf(ncommand,"%[^ ]",item)==1) {
   1.708 +      const int l = cimg::strlen(item);
   1.709 +      command_line.insert(CImg<char>(item,l+1,1,1,1,false));
   1.710 +      ncommand += l;
   1.711 +      while (*ncommand==' ') ++ncommand;
   1.712 +    } else break;
   1.713 +  }
   1.714 +  is_released = true;
   1.715 +  parse(images);
   1.716 +}
   1.717 +
   1.718 +// Display specified image(s).
   1.719 +//-----------------------------
   1.720 +template<typename T>
   1.721 +bool gmic::display_images(const CImgList<T>& images, const CImg<unsigned int>& indices,
   1.722 +                          const bool verbose) const {
   1.723 +  if (!images || !indices) { print("Display image []."); return false; }
   1.724 +  CImgList<unsigned int> inds = indices.get_unroll('x').get_split('x');
   1.725 +  CImgList<T> visu;
   1.726 +  unsigned int max_height = 0;
   1.727 +  cimglist_for(inds,l) {
   1.728 +    const CImg<T>& img = images[inds(l,0)];
   1.729 +    if (img.height>max_height && !img.is_CImg3d()) max_height = img.height;
   1.730 +  }
   1.731 +  cimglist_for(inds,l) {
   1.732 +    const unsigned int ind = inds(l,0);
   1.733 +    const CImg<T> &img = images[ind];
   1.734 +    if (img) {
   1.735 +      if (!max_height || img.height<max_height) visu.insert(img,~0U,true);
   1.736 +      else visu.insert(img.get_lines(0,max_height-1));
   1.737 +    } else if (verbose) { warning("Display image : Image [%d] is empty.",ind); inds.remove(l--); }
   1.738 +  }
   1.739 +  const CImg<unsigned int> nindices = inds.get_append('x');
   1.740 +  const char *const fnames = indices2string(nindices,false);
   1.741 +  print("Display image%s = '%s'.\n\n",gmic_inds,fnames);
   1.742 +  if (visu.size) {
   1.743 +    if (visu.size!=1) visu.display(fnames,verbosity_level>=0,'x','p');
   1.744 +    else {
   1.745 +      const CImg<T> &img = visu[0];
   1.746 +      char title[4096] = { 0 };
   1.747 +      std::sprintf(title,"%s (%dx%dx%dx%d)",fnames,img.dimx(),img.dimy(),img.dimz(),img.dimv());
   1.748 +      img.display(title,verbosity_level>=0);
   1.749 +    }
   1.750 +  }
   1.751 +  return true;
   1.752 +}
   1.753 +
   1.754 +// Display plots of specified image(s).
   1.755 +//--------------------------------------
   1.756 +template<typename T>
   1.757 +bool gmic::display_plots(const CImgList<T>& images, const CImg<unsigned int>& indices,
   1.758 +                         const unsigned int plot_type, const unsigned int vertex_type,
   1.759 +                         const double xmin, const double xmax,
   1.760 +                         const double ymin, const double ymax,
   1.761 +                         const bool verbose) const {
   1.762 +  if (!images || !indices) { print("Plot image []."); return false; }
   1.763 +  CImgDisplay disp(cimg_fitscreen(640,480,1),0,0);
   1.764 +  cimg_foroff(indices,l) {
   1.765 +    const unsigned int ind = indices[l];
   1.766 +    const CImg<T>& img = images[ind];
   1.767 +    if (img) {
   1.768 +      print("Plot image%s = '%s'.\n",gmic_inds,indices2string(indices,false));
   1.769 +      if (verbosity_level>=0) { std::fputc('\n',cimg_stdout); img.print(filenames[ind].ptr()); }
   1.770 +      char title[4096] = { 0 };
   1.771 +      std::sprintf(title,"%s (%dx%dx%dx%d)",
   1.772 +                   filenames[ind].ptr(),img.dimx(),img.dimy(),img.dimz(),img.dimv());
   1.773 +      img.display_graph(disp.set_title(title),plot_type,vertex_type,0,xmin,xmax,0,ymin,ymax);
   1.774 +    } else if (verbose) warning("Plot image : Image [%d] is empty.",ind);
   1.775 +  }
   1.776 +  return true;
   1.777 +}
   1.778 +
   1.779 +// Display specified 3D object(s).
   1.780 +//--------------------------------
   1.781 +template<typename T>
   1.782 +bool gmic::display_objects3d(const CImgList<T>& images, const CImg<unsigned int>& indices,
   1.783 +                             const bool verbose) const {
   1.784 +  if (!indices) { print("Display 3D object []."); return false; }
   1.785 +  CImg<unsigned char> background;
   1.786 +  bool exist3d = false;
   1.787 +  CImgDisplay disp;
   1.788 +  cimg_foroff(indices,l) {
   1.789 +    const unsigned int ind = indices[l];
   1.790 +    const CImg<T> &img = images[ind];
   1.791 +    if (!img.is_CImg3d()) {
   1.792 +      if (verbose) warning("Display 3D object : Image [%d] is not a 3D object.",ind);
   1.793 +    } else {
   1.794 +      exist3d = true;
   1.795 +      if (!background || !disp) {
   1.796 +        background.assign(cimg_fitscreen(640,480,1),1,3);
   1.797 +        cimg_forV(background,k) background.get_shared_channel(k).fill(background3d[k]);
   1.798 +        disp.assign(background);
   1.799 +      }
   1.800 +      CImgList<unsigned int> primitives3d;
   1.801 +      CImgList<unsigned char> colors3d;
   1.802 +      CImg<float> opacities3d;
   1.803 +      CImg<float> points3d(img);
   1.804 +      points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
   1.805 +      print("Display 3D object [%u] = '%s' (%d points, %u primitives).",
   1.806 +            ind,filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
   1.807 +      disp.set_title("%s (%d points, %u primitives)",
   1.808 +                     filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
   1.809 +      background.display_object3d(disp,points3d,primitives3d,colors3d,opacities3d,
   1.810 +                                  true,render3d,renderd3d,!is_oriented3d,focale3d,specular_light3d,specular_shine3d);
   1.811 +      if (disp.is_closed) break;
   1.812 +    }
   1.813 +  }
   1.814 +  return exist3d;
   1.815 +}
   1.816 +
   1.817 +// Substitute '@' expressions.
   1.818 +//----------------------------
   1.819 +template<typename T>
   1.820 +CImg<char> gmic::substitute_arobace(const char *const argument, const CImgList<T>& images) const {
   1.821 +  if (!argument) return CImg<char>();
   1.822 +  CImgList<char> _largument;
   1.823 +  char range[4096] = { 0 };
   1.824 +  for (const char *nargument = argument; *nargument; ) {
   1.825 +    if (*nargument=='@') {
   1.826 +      char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
   1.827 +      int ind = 0, bcond = 0; *range = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, sep = 0, end = 0;
   1.828 +      float x = 0, y = 0, z = 0, v = 0, m = 0, M = 1;
   1.829 +      if (nargument[1]=='#' ||
   1.830 +          (std::sscanf(nargument,"@{#%c",&sep)==1 && sep=='}')) {
   1.831 +        std::sprintf(range,"%u",images.size);
   1.832 +        _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
   1.833 +        if (sep=='}') nargument+=4; else nargument+=2;
   1.834 +      } else if (std::sscanf(nargument,"@%d",&ind)==1 ||
   1.835 +                 (std::sscanf(nargument,"@{%d%c",&ind,&sep)==2 && sep=='}') ||
   1.836 +                 (std::sscanf(nargument,"@{%d%*c%4095[^}]%c",&ind,range,&sep)==3 && sep=='}')) {
   1.837 +        int nind = ind;
   1.838 +        if (nind<0) nind+=images.size;
   1.839 +        if (nind<0 || nind>=(int)images.size) {
   1.840 +          if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
   1.841 +                                 ind,images.size,images.size-1);
   1.842 +          else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
   1.843 +        }
   1.844 +        const unsigned int sizrange = cimg::strlen(range);
   1.845 +        const CImg<T>& img = images[nind];
   1.846 +        CImg<T> values;
   1.847 +        if (sizrange) {
   1.848 +          const CImg<unsigned int> iinds = indices2cimg(range,img.size(),"parsing");
   1.849 +          values.assign(iinds.size());
   1.850 +          cimg_foroff(iinds,p) values[p] = img[iinds(p)];
   1.851 +        } else values = img.get_shared();
   1.852 +        const CImg<char> vs = values.value_string();
   1.853 +        const unsigned int vsl = vs.size();
   1.854 +        if (vsl>1) _largument.insert(CImg<char>(vs.ptr(),vsl-1,1,1,1,true));
   1.855 +        nargument+= 1 + (sep?2:0) + (sizrange?1:0) + sizrange + std::sprintf(range,"%d",ind);
   1.856 +      } else if (((std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%c",&ind,argx,&sep)==3 && sep==')') ||
   1.857 +                  (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind,argx,argy,&sep)==4 && sep==')') ||
   1.858 +                  (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
   1.859 +                               &ind,argx,argy,argz,&sep)==5 && sep==')') ||
   1.860 +                  (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
   1.861 +                               &ind,argx,argy,argz,argv,&sep)==6 && sep==')') ||
   1.862 +                  (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
   1.863 +                               &ind,argx,argy,argz,argv,&bcond,&sep)==7 && sep==')')) &&
   1.864 +                 (!*argx ||
   1.865 +                  (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
   1.866 +                  std::sscanf(argx,"%f%c",&x,&end)==1) &&
   1.867 +                 (!*argy ||
   1.868 +                  (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
   1.869 +                  std::sscanf(argy,"%f%c",&y,&end)==1) &&
   1.870 +                 (!*argz ||
   1.871 +                  (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
   1.872 +                  std::sscanf(argz,"%f%c",&z,&end)==1) &&
   1.873 +                 (!*argv ||
   1.874 +                  (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
   1.875 +                  std::sscanf(argv,"%f%c",&v,&end)==1)) {
   1.876 +        int nind = ind;
   1.877 +        if (nind<0) nind+=images.size;
   1.878 +        if (nind<0 || nind>=(int)images.size) {
   1.879 +          if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
   1.880 +                                 ind,images.size,images.size-1);
   1.881 +          else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
   1.882 +        }
   1.883 +        const CImg<T>& img = images[nind];
   1.884 +        const int
   1.885 +          nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
   1.886 +          ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
   1.887 +          nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
   1.888 +          nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
   1.889 +        std::sprintf(range,"%g",bcond?(double)img.atXYZV(nx,ny,nz,nv):(double)img.atXYZV(nx,ny,nz,nv,0));
   1.890 +        _largument.insert(CImg<char>(range,cimg::strlen(range)));
   1.891 +        while (*nargument!=')') ++nargument; ++nargument;
   1.892 +      } else if ((std::sscanf(nargument,"@%c",&sep)==1 && sep=='?') ||
   1.893 +                 (std::sscanf(nargument,"@{?%c",&sep)==1 && sep=='}') ||
   1.894 +                 (std::sscanf(nargument,"@[?%c",&sep)==1 && sep==']') ||
   1.895 +                 (std::sscanf(nargument,"@{?%*c%f%c",&M,&sep)==2 && sep=='}') ||
   1.896 +                 (std::sscanf(nargument,"@[?%*c%f%c",&M,&sep)==2 && sep==']') ||
   1.897 +                 (std::sscanf(nargument,"@{?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep=='}') ||
   1.898 +                 (std::sscanf(nargument,"@[?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep==']')) {
   1.899 +        const double r = m + (M-m)*cimg::rand();
   1.900 +        if (sep==']') std::sprintf(range,"%d",(int)cimg::round(r,1)); else std::sprintf(range,"%g",r);
   1.901 +        _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
   1.902 +        if (sep=='?') nargument+=2; else { while (*nargument!=sep) ++nargument; ++nargument; }
   1.903 +      } else _largument.insert(CImg<char>(nargument++,1,1,1,1,true));
   1.904 +    } else {
   1.905 +      std::sscanf(nargument,"%4095[^@]",range);
   1.906 +      const int l = cimg::strlen(range);
   1.907 +      _largument.insert(CImg<char>(range,l,1,1,1,true));
   1.908 +      nargument+=l;
   1.909 +    }
   1.910 +  }
   1.911 +  _largument.insert(CImg<char>::vector(0));
   1.912 +  return _largument.get_append('x');
   1.913 +}
   1.914 +
   1.915 +// Main parsing procedure.
   1.916 +//------------------------
   1.917 +template<typename T>
   1.918 +gmic& gmic::parse(CImgList<T> &images) {
   1.919 +  const unsigned int command_line_maxsize = 65535;
   1.920 +  const int no_ind = (int)(~0U>>1);
   1.921 +  cimg::exception_mode() = 0;
   1.922 +
   1.923 +  // Begin command line parsing.
   1.924 +  while (position<command_line.size && command_line.size<command_line_maxsize) {
   1.925 +    const char
   1.926 +      *const orig_item = command_line[position].ptr(),
   1.927 +      *const orig_argument = position+1<command_line.size?command_line[position+1].ptr():"";
   1.928 +
   1.929 +    // Substitute '@' expressions in 'orig_item' and 'orig_argument' if necessary.
   1.930 +    CImg<char> _item, _argument, _argument_text;
   1.931 +    if (*orig_item=='-' || *orig_item=='[' || *orig_item=='(') {
   1.932 +      if (std::strchr(orig_item,'@')) {
   1.933 +        _item = substitute_arobace(orig_item,images);
   1.934 +        if (_item.size()>4095) error("Buffer overflow when substituting item '%s'.",orig_item);
   1.935 +      }
   1.936 +      if (*orig_item=='-' &&
   1.937 +          (*orig_argument!='-' ||
   1.938 +           (*orig_argument=='-' && (orig_argument[1]=='.' || orig_argument[1]=='@' ||
   1.939 +                                    (orig_argument[1]>='0' && orig_argument[1]<='9'))))
   1.940 +          && std::strchr(orig_argument,'@')) {
   1.941 +        _argument = substitute_arobace(orig_argument,images);
   1.942 +        if (_argument.size()>4095) error("Buffer overflow when substituting argument '%s'.",orig_argument);
   1.943 +      }
   1.944 +    }
   1.945 +    const char
   1.946 +      *item = _item?_item.ptr():orig_item,
   1.947 +      *argument = _argument?_argument.ptr():orig_argument;
   1.948 +    const char *argument_text = argument;
   1.949 +    if (cimg::strlen(argument)>=64) {
   1.950 +      _argument_text.assign(64,1,1,1,0);
   1.951 +      std::memcpy(_argument_text.ptr(),argument,60*sizeof(char));
   1.952 +      _argument_text[60] = _argument_text[61] = _argument_text[62] = '.'; _argument_text[63] = 0;
   1.953 +      argument_text = _argument_text.ptr();
   1.954 +    }
   1.955 +
   1.956 +    // Get current item/command from the command line.
   1.957 +    char item0[4096] = { 0 }, item1[4096] = { 0 };
   1.958 +    bool get_version = false;
   1.959 +    CImg<unsigned int> indices;
   1.960 +    if (item[0]=='-' && item[1] && item[1]!='.') {
   1.961 +      char sep0 = 0, sep1 = 0, end = 0;
   1.962 +      if (item[1]=='-' && item[2] && item[2]!='[' && item[2]!='3' && item[3]!='d') { ++item; get_version = true; }
   1.963 +      const int err = std::sscanf(item,"%4095[^[]%c%4095[0-9.eE%,:+-]%c%c",item0,&sep0,item1,&sep1,&end);
   1.964 +      if (err==1) indices = CImg<unsigned int>::sequence(images.size,0,images.size-1);
   1.965 +      else if (err==4 && sep1==']')
   1.966 +        indices = indices2cimg(item1,(!strcmp(item0,"-i")||!strcmp(item0,"-input"))?images.size+1:images.size,item0);
   1.967 +      else { std::strcpy(item0,item); item1[0] = 0; }
   1.968 +    }
   1.969 +    ++position;
   1.970 +
   1.971 +    // Check for verbosity commands.
   1.972 +    if (*item=='-') {
   1.973 +      if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item)) ++verbosity_level;
   1.974 +      else if (!cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) --verbosity_level;
   1.975 +    }
   1.976 +
   1.977 +    if (is_begin) { print("Start G'MIC instance."); is_begin = false; }
   1.978 +    debug("Item : '%s', Argument : '%s'.",item,argument);
   1.979 +
   1.980 +    // Begin command interpretation.
   1.981 +    try {
   1.982 +      if (*item=='-') {
   1.983 +
   1.984 +        //----------------
   1.985 +        // Global options
   1.986 +        //----------------
   1.987 +
   1.988 +        // Verbosity (actually, just continue to next command since verbosity has been already processed above).
   1.989 +        if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item) ||
   1.990 +            !cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) continue;
   1.991 +
   1.992 +        // Load macro file.
   1.993 +        if (!cimg::strcmp("-macros",item) || !cimg::strcmp("-m",item)) {
   1.994 +          print("Load macro file '%s'",cimg::basename(argument));
   1.995 +          std::FILE *const file = cimg::fopen(argument,"r");
   1.996 +          if (file) {
   1.997 +            const unsigned int siz = macros.size;
   1.998 +            add_macros(file,true);
   1.999 +            cimg::fclose(file);
  1.1000 +            if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u macros added).",macros.size-siz);
  1.1001 +          }
  1.1002 +          else error("Load macro file '%s' : File not found.",argument);
  1.1003 +          ++position; continue;
  1.1004 +        }
  1.1005 +
  1.1006 +        // Switch 'is_debug' flag.
  1.1007 +        if (!cimg::strcmp("-debug",item)) {
  1.1008 +          is_debug = !is_debug;
  1.1009 +          continue;
  1.1010 +        }
  1.1011 +
  1.1012 +        // Switch 'is_fullpath' flag.
  1.1013 +        if (!cimg::strcmp("-fullpath",item)) {
  1.1014 +          is_fullpath = !is_fullpath;
  1.1015 +          continue;
  1.1016 +        }
  1.1017 +
  1.1018 +        //----------------------
  1.1019 +        // Arithmetic operators
  1.1020 +        //----------------------
  1.1021 +
  1.1022 +        // Common arithmetic operators.
  1.1023 +        gmic_arithmetic_item("-add","-+",
  1.1024 +                             operator+=,"Add %g to image%s",value,gmic_inds,T,
  1.1025 +                             operator+=,"Add to image%s",
  1.1026 +                             "Add image [%d] to image%s",ind[0],gmic_inds,
  1.1027 +                             "Add image%s together");
  1.1028 +
  1.1029 +        gmic_arithmetic_item("-sub","--",
  1.1030 +                             operator-=,"Substract %g to image%s",value,gmic_inds,T,
  1.1031 +                             operator-=,"Substract to image%s",
  1.1032 +                             "Substract image [%d] to image%s",ind[0],gmic_inds,
  1.1033 +                             "Substract image%s together");
  1.1034 +
  1.1035 +        gmic_arithmetic_item("-mul","-*",
  1.1036 +                             operator*=,"Multiply image%s by %g",gmic_inds,value,double,
  1.1037 +                             mul,"Multiply image%s",
  1.1038 +                             "Multiply image%s by image [%d]",gmic_inds,ind[0],
  1.1039 +                             "Multiply image%s together");
  1.1040 +
  1.1041 +        gmic_arithmetic_item("-div","-/",
  1.1042 +                             operator/=,"Divide image%s by %g",gmic_inds,value,double,
  1.1043 +                             div,"Divide image%s",
  1.1044 +                             "Divide image%s by image [%d]",gmic_inds,ind[0],
  1.1045 +                             "Divide image%s together");
  1.1046 +
  1.1047 +        gmic_arithmetic_item("-pow","-^",
  1.1048 +                             pow,"Compute image%s to the power of %g",gmic_inds,value,double,
  1.1049 +                             pow,"Compute power of image%s",
  1.1050 +                             "Compute image%s to the power of image [%d]",gmic_inds,ind[0],
  1.1051 +                             "Compute the power of image%s together");
  1.1052 +
  1.1053 +        gmic_arithmetic_item("-min","-min",
  1.1054 +                             min,"Compute pointwise minimum between image%s and %g",gmic_inds,value,T,
  1.1055 +                             min,"Compute pointwise minimum with image%s",
  1.1056 +                             "Compute pointwise minimum between image%s and image [%d]",gmic_inds,ind[0],
  1.1057 +                             "Compute pointwise minimum between image%s together");
  1.1058 +
  1.1059 +        gmic_arithmetic_item("-max","-max",
  1.1060 +                             max,"Compute pointwise maximum between image%s and %g",gmic_inds,value,T,
  1.1061 +                             max,"Compute pointwise maximum with image%s",
  1.1062 +                             "Compute pointwise maximum between image%s and image [%d]",gmic_inds,ind[0],
  1.1063 +                             "Compute pointwise maximum between image%s together");
  1.1064 +
  1.1065 +        gmic_arithmetic_item("-mod","-%",
  1.1066 +                             operator%=,"Compute pointwise modulo between image%s and %g.",gmic_inds,value,T,
  1.1067 +                             operator%=,"Compute pointwise modulo with image%s",
  1.1068 +                             "Compute pointwise modulo between image%s and image [%d]",gmic_inds,ind[0],
  1.1069 +                             "Compute pointwise modulo between image%s together");
  1.1070 +
  1.1071 +        gmic_arithmetic_item("-and","-and",
  1.1072 +                             operator&=,"Compute bitwise AND between image%s and %g.",gmic_inds,value,unsigned int,
  1.1073 +                             operator&=,"Compute bitwise AND with image%s",
  1.1074 +                             "Compute bitwise AND between image%s and image [%d]",gmic_inds,ind[0],
  1.1075 +                             "Compute bitwise AND between image%s together");
  1.1076 +
  1.1077 +        gmic_arithmetic_item("-or","-or",
  1.1078 +                             operator|=,"Compute bitwise OR between image%s and %g.",gmic_inds,value,unsigned int,
  1.1079 +                             operator|=,"Compute bitwise OR with image%s",
  1.1080 +                             "Compute bitwise OR between image%s and image [%d]",gmic_inds,ind[0],
  1.1081 +                             "Compute bitwise OR between image%s together");
  1.1082 +
  1.1083 +        gmic_arithmetic_item("-xor","-xor",
  1.1084 +                             operator^=,"Compute bitwise XOR between image%s and %g.",gmic_inds,value,unsigned int,
  1.1085 +                             operator^=,"Compute bitwise XOR with image%s",
  1.1086 +                             "Compute bitwise XOR between image%s and image [%d]",gmic_inds,ind[0],
  1.1087 +                             "Compute bitwise XOR between image%s together");
  1.1088 +
  1.1089 +        // Other arithmetic operators.
  1.1090 +        gmic_simple_item("-cos",cos,"Compute cosine of image%s.");
  1.1091 +        gmic_simple_item("-sin",sin,"Compute sine of image%s.");
  1.1092 +        gmic_simple_item("-tan",tan,"Compute tangent of image%s.");
  1.1093 +        gmic_simple_item("-acos",acos,"Compute arccosine of image%s.");
  1.1094 +        gmic_simple_item("-asin",asin,"Compute arcsine of image%s.");
  1.1095 +        gmic_simple_item("-atan",atan,"Compute arctangent of image%s.");
  1.1096 +        gmic_simple_item("-abs",abs,"Compute absolute value of image%s.");
  1.1097 +        gmic_simple_item("-sqr",sqr,"Compute square function of image%s.");
  1.1098 +        gmic_simple_item("-sqrt",sqrt,"Compute square root of image%s.");
  1.1099 +        gmic_simple_item("-exp",exp,"Compute exponential of image%s.");
  1.1100 +        gmic_simple_item("-log",log,"Compute logarithm of image%s.");
  1.1101 +        gmic_simple_item("-log10",log10,"Compute logarithm_10 of image%s.");
  1.1102 +
  1.1103 +        //---------------------------------------
  1.1104 +        // Pointwise operations on pixel values
  1.1105 +        //---------------------------------------
  1.1106 +
  1.1107 +        // Type cast.
  1.1108 +        if (!cimg::strcmp("-type",item) || !cimg::strcmp("-t",item)) {
  1.1109 +          typedef unsigned char uchar;
  1.1110 +          typedef unsigned short ushort;
  1.1111 +          typedef unsigned int uint;
  1.1112 +#ifndef gmic_minimal
  1.1113 +          gmic_cast(bool,"bool");
  1.1114 +          gmic_cast(uchar,"unsigned char");
  1.1115 +          gmic_cast(char,"char");
  1.1116 +          gmic_cast(ushort,"unsigned short");
  1.1117 +          gmic_cast(short,"short");
  1.1118 +          gmic_cast(uint,"unsigned int");
  1.1119 +          gmic_cast(int,"int");
  1.1120 +          gmic_cast(double,"double");
  1.1121 +#endif
  1.1122 +          gmic_cast(float,"float");
  1.1123 +          error("Set pixel type : Invalid argument '%s' "
  1.1124 +                "(should be '{bool,uchar,char,ushort,short,uint,int,float,double}').",
  1.1125 +                argument_text);
  1.1126 +        }
  1.1127 +
  1.1128 +        // Set scalar value.
  1.1129 +        if (!cimg::strcmp("-set",item0) || !cimg::strcmp("-=",item0)) {
  1.1130 +          double value = 0; float x = 0, y = 0, z = 0, v = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
  1.1131 +          char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
  1.1132 +          if ((std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1.1133 +               std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%c",&value,argx,&end)==2 ||
  1.1134 +               std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,&end)==3 ||
  1.1135 +               std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,argz,&end)==4 ||
  1.1136 +               std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,argz,argv,&end)==5) &&
  1.1137 +              (!*argx ||
  1.1138 +               (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
  1.1139 +               std::sscanf(argx,"%f%c",&x,&end)==1) &&
  1.1140 +              (!*argy ||
  1.1141 +               (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
  1.1142 +               std::sscanf(argy,"%f%c",&y,&end)==1) &&
  1.1143 +              (!*argz ||
  1.1144 +               (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
  1.1145 +               std::sscanf(argz,"%f%c",&z,&end)==1) &&
  1.1146 +              (!*argv ||
  1.1147 +               (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
  1.1148 +               std::sscanf(argv,"%f%c",&v,&end)==1)) {
  1.1149 +            print("Set scalar value %g at position (%g%s,%g%s,%g%s,%g%s) in image%s",
  1.1150 +                  value,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",v,sepv=='%'?"%":"",gmic_inds);
  1.1151 +            cimg_foroff(indices,l) {
  1.1152 +              CImg<T> &img = images[indices[l]];
  1.1153 +              const int
  1.1154 +                nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  1.1155 +                ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  1.1156 +                nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
  1.1157 +                nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
  1.1158 +              gmic_apply(images[indices[l]],gmic_set(value,nx,ny,nz,nv));
  1.1159 +            }
  1.1160 +          } else error("Set scalar value in image%s : Invalid argument '%s' "
  1.1161 +                       "(should be 'value,x[,y[,z[,v]]]').",
  1.1162 +                       argument_text);
  1.1163 +          ++position; continue;
  1.1164 +        }
  1.1165 +
  1.1166 +        // Invert endianness.
  1.1167 +        gmic_simple_item("-endian",invert_endianness,"Invert endianness of image%s.");
  1.1168 +
  1.1169 +        // Fill.
  1.1170 +        if (!cimg::strcmp("-fill",item0) || !cimg::strcmp("-f",item0)) {
  1.1171 +          char sep = 0, end = 0; int ind0 = no_ind;
  1.1172 +          if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  1.1173 +            gmic_check_indice(ind0,"Fill image%s");
  1.1174 +            print("Fill image%s with values of image [%d].",gmic_inds,ind0);
  1.1175 +            const CImg<T> values = images[ind0];
  1.1176 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(values));
  1.1177 +          } else {
  1.1178 +            print("Fill image%s with values '%s'.",gmic_inds,argument_text);
  1.1179 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(argument,true));
  1.1180 +          }
  1.1181 +          ++position; continue;
  1.1182 +        }
  1.1183 +
  1.1184 +        // Threshold.
  1.1185 +        if (!cimg::strcmp("-threshold",item0)) {
  1.1186 +          char sep = 0, end = 0; int soft = 0; double value = 0;
  1.1187 +          if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1.1188 +              (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%') ||
  1.1189 +              std::sscanf(argument,"%lf%*c%d%c",&value,&soft,&end)==2 ||
  1.1190 +              (std::sscanf(argument,"%lf%c%*c%d%c",&value,&sep,&soft,&end)==3 && sep=='%')) {
  1.1191 +            print("Threshold image%s with %g%s (%s threshold).",gmic_inds,value,sep=='%'?"%":"",soft?"soft":"hard");
  1.1192 +            cimg_foroff(indices,l) {
  1.1193 +              CImg<T> &img = images[indices[l]];
  1.1194 +              double vmin = 0, vmax = 0, nvalue = value;
  1.1195 +              if (sep=='%') { vmin = img.minmax(vmax); nvalue = vmin + (vmax - vmin)*value/100; }
  1.1196 +              gmic_apply(img,threshold((T)nvalue,soft?true:false));
  1.1197 +            }
  1.1198 +            ++position;
  1.1199 +          } else {
  1.1200 +            print("Threshold image%s : Interactive mode.",gmic_inds);
  1.1201 +            CImgDisplay disp;
  1.1202 +            char title[4096] = { 0 };
  1.1203 +            cimg_foroff(indices,l) {
  1.1204 +              CImg<T>
  1.1205 +                &img = images[indices[l]],
  1.1206 +                visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
  1.1207 +                channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
  1.1208 +              if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
  1.1209 +              else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
  1.1210 +              double
  1.1211 +                vmin = 0, vmax = (double)img.maxmin(vmin),
  1.1212 +                distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0)),
  1.1213 +                amount = 50;
  1.1214 +              bool stopflag = false, obutt = false;
  1.1215 +              int omx = -1, omy = -1;
  1.1216 +              CImg<T> res;
  1.1217 +              for (disp.show().button = disp.key = 0; !stopflag; ) {
  1.1218 +                const unsigned int key = disp.key;
  1.1219 +                if (!res) {
  1.1220 +                  std::sprintf(title,"%s : Interactive threshold %.3g%%",filenames[indices[l]].ptr(),amount);
  1.1221 +                  disp.display(res=visu.get_threshold((T)(vmin + amount*(vmax-vmin)/100))).
  1.1222 +                    set_title(title).wait();
  1.1223 +                }
  1.1224 +                const int mx = disp.mouse_x, my = disp.mouse_y;
  1.1225 +                if (disp.button && mx>=0 && my>=0) {
  1.1226 +                  if (omx==mx && omy==my && !obutt) break;
  1.1227 +                  omx = mx; omy = my; obutt = true;
  1.1228 +                  const double dist = std::sqrt((double)cimg::sqr(mx) + cimg::sqr(my));
  1.1229 +                  amount = dist*100/distmax;
  1.1230 +                  res.assign();
  1.1231 +                } else if (!disp.button) obutt = false;
  1.1232 +                if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
  1.1233 +                if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
  1.1234 +                    (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
  1.1235 +                  disp.is_resized = true;
  1.1236 +                if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
  1.1237 +                    (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
  1.1238 +                  disp.is_resized = true;
  1.1239 +                if (disp.is_resized) {
  1.1240 +                  disp.resize(false).display(res);
  1.1241 +                  distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0));
  1.1242 +                }
  1.1243 +              }
  1.1244 +              gmic_apply(img,threshold((T)(vmin + amount*(vmax-vmin)/100)));
  1.1245 +            }
  1.1246 +          }
  1.1247 +          continue;
  1.1248 +        }
  1.1249 +
  1.1250 +        // Cut.
  1.1251 +        if (!cimg::strcmp("-cut",item0)) {
  1.1252 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1253 +          double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1254 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1255 +              ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1256 +               (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1257 +               std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
  1.1258 +              ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1259 +               (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1260 +               std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
  1.1261 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Cut image%s"); value0 = images[ind0].min(); sep0 = 0; }
  1.1262 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Cut image%s"); value1 = images[ind1].max(); sep1 = 0; }
  1.1263 +            print("Cut image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
  1.1264 +            cimg_foroff(indices,l) {
  1.1265 +              CImg<T> &img = images[indices[l]];
  1.1266 +              double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
  1.1267 +              if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
  1.1268 +              if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
  1.1269 +              gmic_apply(img,cut((T)nvalue0,(T)nvalue1));
  1.1270 +            }
  1.1271 +            ++position;
  1.1272 +          } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
  1.1273 +            if (ind0!=no_ind) gmic_check_indice(ind0,"Cut image%s");
  1.1274 +            value0 = images[ind0].minmax(value1);
  1.1275 +            print("Cut image%s in [%g,%g].",gmic_inds,value0,value1);
  1.1276 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],cut((T)value0,(T)value1));
  1.1277 +            ++position;
  1.1278 +          } else {
  1.1279 +            print("Cut image%s : Interactive mode.",gmic_inds);
  1.1280 +            CImgDisplay disp;
  1.1281 +            char title[4096] = { 0 };
  1.1282 +            cimg_foroff(indices,l) {
  1.1283 +              CImg<T>
  1.1284 +                &img = images[indices[l]],
  1.1285 +                visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
  1.1286 +                channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
  1.1287 +              if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
  1.1288 +              else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
  1.1289 +              double vmin = 0, vmax = (double)img.maxmin(vmin), amount0 = 0, amount1 = 100;
  1.1290 +              bool stopflag = false, obutt = false;
  1.1291 +              int omx = -1, omy = -1;
  1.1292 +              CImg<T> res;
  1.1293 +              for (disp.show().button = disp.key = 0; !stopflag; ) {
  1.1294 +                const unsigned int key = disp.key;
  1.1295 +                if (!res) {
  1.1296 +                  std::sprintf(title,"%s : Interactive cut [%.3g%%,%.3g%%]",
  1.1297 +                               filenames[indices[l]].ptr(),amount0,amount1);
  1.1298 +                  disp.display(res = visu.get_cut((T)(vmin + amount0*(vmax-vmin)/100),
  1.1299 +                                                  (T)(vmin + amount1*(vmax-vmin)/100))).
  1.1300 +                    set_title(title).wait();
  1.1301 +                }
  1.1302 +                const int mx = disp.mouse_x, my = disp.mouse_y;
  1.1303 +                if (disp.button && mx>=0 && my>=0) {
  1.1304 +                  if (omx==mx && omy==my && !obutt) break;
  1.1305 +                  omx = mx; omy = my; obutt = true;
  1.1306 +                  amount0 = mx*100/disp.dimx(); amount1 = my*100/disp.dimy();
  1.1307 +                  res.assign();
  1.1308 +                } else if (!disp.button) obutt = false;
  1.1309 +                if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
  1.1310 +                if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
  1.1311 +                    (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
  1.1312 +                  disp.is_resized = true;
  1.1313 +                if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
  1.1314 +                    (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
  1.1315 +                  disp.is_resized = true;
  1.1316 +                if (disp.is_resized) disp.resize(false).display(res);
  1.1317 +              }
  1.1318 +              gmic_apply(img,cut((T)(vmin + amount0*(vmax-vmin)/100),(T)(vmin + amount1*(vmax-vmin)/100)));
  1.1319 +            }
  1.1320 +          }
  1.1321 +          continue;
  1.1322 +        }
  1.1323 +
  1.1324 +        // Normalize.
  1.1325 +        if (!cimg::strcmp("-normalize",item0) || !cimg::strcmp("-n",item0)) {
  1.1326 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1327 +          double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1328 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1329 +              ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1330 +               (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1331 +               std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
  1.1332 +              ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1333 +               (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1334 +               std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
  1.1335 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Normalize image%s"); value0 = images[ind0].min(); sep0 = 0; }
  1.1336 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Normalize image%s"); value1 = images[ind1].max(); sep1 = 0; }
  1.1337 +            print("Normalize image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
  1.1338 +            cimg_foroff(indices,l) {
  1.1339 +              CImg<T> &img = images[indices[l]];
  1.1340 +              double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
  1.1341 +              if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
  1.1342 +              if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
  1.1343 +              gmic_apply(img,normalize((T)nvalue0,(T)nvalue1));
  1.1344 +            }
  1.1345 +          } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
  1.1346 +            if (ind0!=no_ind) gmic_check_indice(ind0,"Normalize image%s");
  1.1347 +            value0 = images[ind0].minmax(value1);
  1.1348 +            print("Normalize image%s in [%g,%g].",gmic_inds,value0,value1);
  1.1349 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],normalize((T)value0,(T)value1));
  1.1350 +          } else error("Normalize image%s : Invalid argument '%s' "
  1.1351 +                       "(should be '{value1[%%],[indice]},{value2[%%],[indice]}').",gmic_inds,argument_text);
  1.1352 +          ++position; continue;
  1.1353 +        }
  1.1354 +
  1.1355 +        // Round.
  1.1356 +        if (!cimg::strcmp("-round",item0)) {
  1.1357 +          char end = 0; double value = 0; int rtype = 0;
  1.1358 +          if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1.1359 +              std::sscanf(argument,"%lf%*c%d%c",&value,&rtype,&end)==2) {
  1.1360 +            print("Round image%s with value %g (%s rounding).",
  1.1361 +                  gmic_inds,value,rtype<0?"backward":rtype>0?"forward":"nearest");
  1.1362 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],round((float)value,rtype));
  1.1363 +          } else error("Round image%s : Invalid argument '%s' "
  1.1364 +                       "(should be 'round_value[,round_type]').",gmic_inds,argument_text);
  1.1365 +          ++position; continue;
  1.1366 +        }
  1.1367 +
  1.1368 +        // Equalize histogram.
  1.1369 +        if (!cimg::strcmp("-equalize",item0)) {
  1.1370 +          float nb = 256; char sep = 0, end = 0;
  1.1371 +          if (std::sscanf(argument,"%f%c",&nb,&end)==1 ||
  1.1372 +              (std::sscanf(argument,"%f%c%c",&nb,&sep,&end)==2 && sep=='%')) {
  1.1373 +            if (nb<=0) error("Equalize image%s : Invalid cluster number %g.",gmic_inds,nb);
  1.1374 +            print("Equalize image%s with %g%s clusters.",gmic_inds,nb,sep=='%'?"%":"");
  1.1375 +            cimg_foroff(indices,l) {
  1.1376 +              CImg<T>& img = images[indices[l]];
  1.1377 +              unsigned int N = (unsigned int)nb;
  1.1378 +              if (sep=='%') { double m, M = img.maxmin(m); N = (unsigned int)cimg::round((M-m)*nb/100,1); }
  1.1379 +              gmic_apply(img,equalize((int)nb));
  1.1380 +            }
  1.1381 +          } else error("Equalize image%s : Invalid argument '%s' "
  1.1382 +                       "(should be 'nb_clusters[%%]').",gmic_inds,argument_text);
  1.1383 +          ++position; continue;
  1.1384 +        }
  1.1385 +
  1.1386 +        // Quantize.
  1.1387 +        if (!cimg::strcmp("-quantize",item0)) {
  1.1388 +          int nb = 0; char end = 0;
  1.1389 +          if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1.1390 +            print("Quantize image%s with %d levels.",gmic_inds,nb);
  1.1391 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],quantize(nb));
  1.1392 +          } else error("Quantize image%s : Invalid argument '%s' "
  1.1393 +                       "(should be 'nb_levels').",gmic_inds,argument_text);
  1.1394 +          ++position; continue;
  1.1395 +        }
  1.1396 +
  1.1397 +        // Add noise.
  1.1398 +        if (!cimg::strcmp("-noise",item0)) {
  1.1399 +          float sigma = 0; char sep = 0, end = 0; int ntype = 0;
  1.1400 +          if (std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
  1.1401 +              (std::sscanf(argument,"%f%c%c",&sigma,&sep,&end)==2 && sep=='%') ||
  1.1402 +              std::sscanf(argument,"%f%*c%d%c",&sigma,&ntype,&end)==2 ||
  1.1403 +              (std::sscanf(argument,"%f%c%*c%d%c",&sigma,&sep,&ntype,&end)==3 && sep=='%')) {
  1.1404 +            const char *st_type = ntype==0?"gaussian":ntype==1?"uniform":ntype==2?"salt&pepper":"poisson";
  1.1405 +            if (sep=='%') sigma = -sigma;
  1.1406 +            print("Add %s noise with standard deviation %g%s to image%s.",
  1.1407 +                  st_type,cimg::abs(sigma),sigma<0?"%":"",gmic_inds);
  1.1408 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],noise(sigma,ntype));
  1.1409 +          } else error("Add noise to image%s : Invalid argument '%s' "
  1.1410 +                       "(should be 'std[%%][,noise_type]').",gmic_inds,argument_text);
  1.1411 +          ++position; continue;
  1.1412 +        }
  1.1413 +
  1.1414 +        // Rand.
  1.1415 +        if (!cimg::strcmp("-rand",item0)) {
  1.1416 +          double value0 = 0, value1 = 0; char end = 0;
  1.1417 +          if (std::sscanf(argument,"%lf%*c%lf%c",&value0,&value1,&end)==2) {
  1.1418 +            print("Fill image%s with random values in [%g,%g].",gmic_inds,value0,value1);
  1.1419 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],rand((T)value0,(T)value1));
  1.1420 +          } else error("Fill image%s with random values : Invalid argument '%s' "
  1.1421 +                       "(should be 'valmin,valmax').",gmic_inds,argument_text);
  1.1422 +          ++position; continue;
  1.1423 +        }
  1.1424 +
  1.1425 +        // Compute pointwise norms and orientations.
  1.1426 +        gmic_simple_item("-norm",pointwise_norm,"Compute vector norm.");
  1.1427 +        gmic_simple_item("-orientation",pointwise_orientation,"Compute vector orientation.");
  1.1428 +
  1.1429 +        //------------------------
  1.1430 +        // Color base conversions
  1.1431 +        //------------------------
  1.1432 +        gmic_simple_item("-rgb2hsv",RGBtoHSV,"Convert image%s from RGB to HSV colorbases.");
  1.1433 +        gmic_simple_item("-rgb2hsl",RGBtoHSL,"Convert image%s from RGB to HSL colorbases.");
  1.1434 +        gmic_simple_item("-rgb2hsi",RGBtoHSI,"Convert image%s from RGB to HSI colorbases.");
  1.1435 +        gmic_simple_item("-rgb2yuv",RGBtoYUV,"Convert image%s from RGB to YUV colorbases.");
  1.1436 +        gmic_simple_item("-rgb2ycbcr",RGBtoYCbCr,"Convert image%s from RGB to YCbCr colorbases.");
  1.1437 +        gmic_simple_item("-rgb2xyz",RGBtoXYZ,"Convert image%s from RGB to XYZ colorbases.");
  1.1438 +        gmic_simple_item("-rgb2lab",RGBtoLab,"Convert image%s from RGB to Lab colorbases.");
  1.1439 +        gmic_simple_item("-rgb2cmy",RGBtoCMY,"Convert image%s from RGB to CMY colorbases.");
  1.1440 +        gmic_simple_item("-rgb2cmyk",RGBtoCMYK,"Convert image%s from RGB to CMYK colorbases.");
  1.1441 +        gmic_simple_item("-cmyk2rgb",CMYKtoRGB,"Convert image%s from CMYK to RGB colorbases.");
  1.1442 +        gmic_simple_item("-cmy2rgb",CMYtoRGB,"Convert image%s from CMY to RGB colorbases.");
  1.1443 +        gmic_simple_item("-lab2rgb",LabtoRGB,"Convert image%s from Lab to RGB colorbases.");
  1.1444 +        gmic_simple_item("-xyz2rgb",XYZtoRGB,"Convert image%s from XYZ to RGB colorbases.");
  1.1445 +        gmic_simple_item("-ycbcr2rgb",YCbCrtoRGB,"Convert image%s from YCbCr to RGB colorbases.");
  1.1446 +        gmic_simple_item("-yuv2rgb",YUVtoRGB,"Convert image%s from YUV to RGB colorbases.");
  1.1447 +        gmic_simple_item("-hsi2rgb",HSItoRGB,"Convert image%s from HSI to RGB colorbases.");
  1.1448 +        gmic_simple_item("-hsl2rgb",HSLtoRGB,"Convert image%s from HSL to RGB colorbases.");
  1.1449 +        gmic_simple_item("-hsv2rgb",HSVtoRGB,"Convert image%s from HSV to RGB colorbases.");
  1.1450 +
  1.1451 +        // Apply LUT.
  1.1452 +        if (!cimg::strcmp("-lut2rgb",item0)) {
  1.1453 +          int nb = 0, ind0 = 0; char sep = 0, end = 0;
  1.1454 +          if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  1.1455 +            gmic_check_indice(ind0,"Map LUT on image%s");
  1.1456 +            print("Map LUT [%d] on image%s.",ind0,gmic_inds);
  1.1457 +            const CImg<T> palette = images[ind0];
  1.1458 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().LUTtoRGB(palette));
  1.1459 +          } else if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1.1460 +            if (nb<0 || nb>2) error("Map LUT on image%s : Invalid LUT number %d.",gmic_inds,nb);
  1.1461 +            print("Map %s LUT on image%s.",nb==0?"default":nb==1?"rainbow":"cluster",gmic_inds);
  1.1462 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().
  1.1463 +                                              LUTtoRGB(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8()));
  1.1464 +          } else error("Map LUT on image%s : Invalid argument '%s' "
  1.1465 +                       "(should be '[indice]' or '{0,1,2}').",gmic_inds,argument_text);
  1.1466 +          ++position; continue;
  1.1467 +        }
  1.1468 +
  1.1469 +        // Convert to LUT.
  1.1470 +        if (!cimg::strcmp("-rgb2lut",item0)) {
  1.1471 +          int nb = 0, ind0 = 0, dithering = 0; char sep = 0, end = 0;
  1.1472 +          if (std::sscanf(argument,"[%d%c%*c%d",&ind0,&sep,&dithering)>=2 && sep==']') {
  1.1473 +            gmic_check_indice(ind0,"Index image%s with LUT");
  1.1474 +            print("Index image%s with LUT [%d] %s dithering.",gmic_inds,ind0,dithering?"with":"without");
  1.1475 +            const CImg<T> palette = images[ind0];
  1.1476 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],RGBtoLUT(palette,dithering,true));
  1.1477 +          } else if (std::sscanf(argument,"%d%*c%d%c",&nb,&dithering,&end)==2 ||
  1.1478 +                     std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1.1479 +            if (nb<0 || nb>2) error("Index image%s with LUT : Invalid LUT number %d.",gmic_inds,nb);
  1.1480 +            print("Index image%s with %s LUT.",gmic_inds,nb==0?"default":nb==1?"rainbow":"cluster");
  1.1481 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],
  1.1482 +                                              RGBtoLUT(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8(),
  1.1483 +                                                       dithering,true));
  1.1484 +          } else error("Index image%s with LUT : Invalid argument '%s' "
  1.1485 +                       "(should be '[indice][,dithering]', or '{0,1,2}[,dithering]').",gmic_inds,argument_text);
  1.1486 +          ++position; continue;
  1.1487 +        }
  1.1488 +
  1.1489 +        //-----------------------
  1.1490 +        // Geometric manipulation
  1.1491 +        //-----------------------
  1.1492 +
  1.1493 +        // Resize.
  1.1494 +        if (!cimg::strcmp("-resize",item0) || !cimg::strcmp("-r",item0)) {
  1.1495 +          char
  1.1496 +            sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0,
  1.1497 +            argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
  1.1498 +            float valx = 0, valy = 0, valz = 0, valv = 0;
  1.1499 +            int interpolation = 1, borders = -1, center = 0, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
  1.1500 +            if ((std::sscanf(argument,"[%d%c%c",&indx,&sepx,&end)==2 ||
  1.1501 +                 std::sscanf(argument,"[%d%c%*c%d%c",&indx,&sepx,&interpolation,&end)==3 ||
  1.1502 +                 std::sscanf(argument,"[%d%c%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&end)==4 ||
  1.1503 +                 std::sscanf(argument,"[%d%c%*c%d%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&center,&end)==5)
  1.1504 +                && sepx==']') {
  1.1505 +              gmic_check_indice(indx,"Resize image%s");
  1.1506 +              const int
  1.1507 +                ivalx = images[indx].dimx(),
  1.1508 +                ivaly = images[indx].dimy(),
  1.1509 +                ivalz = images[indx].dimz(),
  1.1510 +                ivalv = images[indx].dimv();
  1.1511 +              print("Resize image%s to %dx%dx%dx%d with %s interpolation.",
  1.1512 +                    gmic_inds,ivalx,ivaly,ivalz,ivalv,
  1.1513 +                    interpolation==0?"no":interpolation==1?"nearest neighbor":
  1.1514 +                    interpolation==2?"moving average":interpolation==3?"linear":
  1.1515 +                    interpolation==4?"grid":"cubic");
  1.1516 +              cimg_foroff(indices,l) gmic_apply(images[indices[l]],resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
  1.1517 +              ++position;
  1.1518 +            } else if (((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%*c%d%*c%d%c",
  1.1519 +                                     argx,argy,argz,argv,&(interpolation=1),&(borders=-1),&(center=0),&end)==7 ||
  1.1520 +                         std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%*c%d%c",
  1.1521 +                                     argx,argy,argz,argv,&interpolation,&borders,&end)==6 ||
  1.1522 +                         std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%c",
  1.1523 +                                     argx,argy,argz,argv,&interpolation,&end)==5 ||
  1.1524 +                         std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1.1525 +                                     argx,argy,argz,argv,&end)==4) &&
  1.1526 +                        ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1.1527 +                         (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1.1528 +                         std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1.1529 +                        ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1.1530 +                         (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1.1531 +                         std::sscanf(argy,"%f%c",&valy,&end)==1) &&
  1.1532 +                        ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
  1.1533 +                         (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
  1.1534 +                         std::sscanf(argz,"%f%c",&valz,&end)==1) &&
  1.1535 +                        ((std::sscanf(argv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']') ||
  1.1536 +                         (std::sscanf(argv,"%f%c%c",&valv,&sepv,&end)==2 && sepv=='%') ||
  1.1537 +                         std::sscanf(argv,"%f%c",&valv,&end)==1)) ||
  1.1538 +                       ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1.1539 +                                     argx,argy,argz,&end)==3) &&
  1.1540 +                        ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1.1541 +                         (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1.1542 +                         std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1.1543 +                        ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1.1544 +                         (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1.1545 +                         std::sscanf(argy,"%f%c",&valy,&end)==1) &&
  1.1546 +                        ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
  1.1547 +                         (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
  1.1548 +                         std::sscanf(argz,"%f%c",&valz,&end)==1)) ||
  1.1549 +                       ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1.1550 +                                     argx,argy,&end)==2) &&
  1.1551 +                        ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1.1552 +                         (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1.1553 +                         std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1.1554 +                        ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1.1555 +                         (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1.1556 +                         std::sscanf(argy,"%f%c",&valy,&end)==1)) ||
  1.1557 +                       ((std::sscanf(argument,"%4095[][0-9.eE%+-]%c",
  1.1558 +                                     argx,&end)==1) &&
  1.1559 +                        ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1.1560 +                         (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1.1561 +                         std::sscanf(argx,"%f%c",&valx,&end)==1))) {
  1.1562 +              if (indx!=no_ind) { gmic_check_indice(indx,"Resize image%s"); valx = (float)images[indx].dimx(); sepx = 0; }
  1.1563 +              if (indy!=no_ind) { gmic_check_indice(indy,"Resize image%s"); valy = (float)images[indy].dimy(); sepy = 0; }
  1.1564 +              if (indz!=no_ind) { gmic_check_indice(indz,"Resize image%s"); valz = (float)images[indz].dimz(); sepz = 0; }
  1.1565 +              if (indv!=no_ind) { gmic_check_indice(indv,"Resize image%s"); valv = (float)images[indv].dimv(); sepv = 0; }
  1.1566 +              if (!valx) { valx = 100; sepx = '%'; }
  1.1567 +              if (!valy) { valy = 100; sepy = '%'; }
  1.1568 +              if (!valz) { valz = 100; sepz = '%'; }
  1.1569 +              if (!valv) { valv = 100; sepv = '%'; }
  1.1570 +              print("Resize image%s to %g%s%g%s%g%s%g%s with %s interpolation.",
  1.1571 +                    gmic_inds,valx,sepx=='%'?"%x":"x",valy,sepy=='%'?"%x":"x",valz,
  1.1572 +                    sepz=='%'?"%x":"x",valv,sepv=='%'?"% ":" ",
  1.1573 +                    interpolation==0?"no":interpolation==1?"nearest neighbor":
  1.1574 +                    interpolation==2?"moving average":interpolation==3?"linear":
  1.1575 +                    interpolation==4?"grid":"cubic");
  1.1576 +
  1.1577 +              cimg_foroff(indices,l) {
  1.1578 +                CImg<T>& img = images[indices[l]];
  1.1579 +                const int
  1.1580 +                  ivalx0 = (int)cimg::round(sepx=='%'?valx*img.dimx()/100:valx,1),
  1.1581 +                  ivaly0 = (int)cimg::round(sepy=='%'?valy*img.dimy()/100:valy,1),
  1.1582 +                  ivalz0 = (int)cimg::round(sepz=='%'?valz*img.dimz()/100:valz,1),
  1.1583 +                  ivalv0 = (int)cimg::round(sepv=='%'?valv*img.dimv()/100:valv,1),
  1.1584 +                  ivalx = ivalx0?ivalx0:1,
  1.1585 +                  ivaly = ivaly0?ivaly0:1,
  1.1586 +                  ivalz = ivalz0?ivalz0:1,
  1.1587 +                  ivalv = ivalv0?ivalv0:1;
  1.1588 +                gmic_apply(img,resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
  1.1589 +              }
  1.1590 +              ++position;
  1.1591 +            } else {
  1.1592 +              print("Resize image%s : Interactive mode.",gmic_inds);
  1.1593 +              char title[4096] = { 0 };
  1.1594 +              cimg_foroff(indices,l) {
  1.1595 +                CImg<T>& img = images[indices[l]];
  1.1596 +                CImgDisplay disp(img,0,1);
  1.1597 +                std::sprintf(title,"%s : Interactive resize",filenames[indices[l]].ptr());
  1.1598 +                disp.set_title(title);
  1.1599 +                img.get_select(0,disp);
  1.1600 +                print("Resize image [%d] to %dx%d.",indices[l],disp.dimx(),disp.dimy());
  1.1601 +                gmic_apply(img,resize(disp));
  1.1602 +              }
  1.1603 +            }
  1.1604 +            continue;
  1.1605 +        }
  1.1606 +
  1.1607 +        // Resize2x. and Resize3x.
  1.1608 +        gmic_simple_item("-resize2x",resize_doubleXY,"Resize image%s using Scale2x algorithm.");
  1.1609 +        gmic_simple_item("-resize3x",resize_doubleXY,"Resize image%s using Scale3x algorithm.");
  1.1610 +
  1.1611 +        // Crop.
  1.1612 +        if (!cimg::strcmp("-crop",item0) || !cimg::strcmp("-c",item0)) {
  1.1613 +          char st0[4096] = { 0 }, st1[4096] = { 0 }, st2[4096] = { 0 }, st3[4096] = { 0 };
  1.1614 +          char st4[4096] = { 0 }, st5[4096] = { 0 }, st6[4096] = { 0 }, st7[4096] = { 0 };
  1.1615 +          char sep0 = 0, sep1 = 0, sep2 = 0, sep3 = 0, sep4 = 0, sep5 = 0, sep6 = 0, sep7 = 0, end = 0;
  1.1616 +          float a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0; int borders = 0;
  1.1617 +          if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1618 +                           "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1.1619 +                           st0,st1,st2,st3,st4,st5,st6,st7,&borders,&end)==9 ||
  1.1620 +               std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1621 +                           "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1.1622 +                           st0,st1,st2,st3,st4,st5,st6,st7,&end)==8) &&
  1.1623 +              (std::sscanf(st0,"%f%c",&a0,&end)==1 || (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1.1624 +              (std::sscanf(st1,"%f%c",&a1,&end)==1 || (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1.1625 +              (std::sscanf(st2,"%f%c",&a2,&end)==1 || (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1.1626 +              (std::sscanf(st3,"%f%c",&a3,&end)==1 || (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
  1.1627 +              (std::sscanf(st4,"%f%c",&a4,&end)==1 || (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
  1.1628 +              (std::sscanf(st5,"%f%c",&a5,&end)==1 || (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%')) &&
  1.1629 +              (std::sscanf(st6,"%f%c",&a6,&end)==1 || (std::sscanf(st6,"%f%c%c",&a6,&sep6,&end)==2 && sep6=='%')) &&
  1.1630 +              (std::sscanf(st7,"%f%c",&a7,&end)==1 || (std::sscanf(st7,"%f%c%c",&a7,&sep7,&end)==2 && sep7=='%'))) {
  1.1631 +            print("Crop image%s with (%g%s%g%s%g%s%g%s x (%g%s%g%s%g%s%g%s.",gmic_inds,
  1.1632 +                  a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",
  1.1633 +                  a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")",
  1.1634 +                  a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%,":",",
  1.1635 +                  a6,sep6=='%'?"%,":",",a7,sep7=='%'?"%)":")");
  1.1636 +            cimg_foroff(indices,l) {
  1.1637 +              CImg<T> &img = images[indices[l]];
  1.1638 +              const int
  1.1639 +                x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1.1640 +                y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1.1641 +                z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
  1.1642 +                v0 = (int)cimg::round(sep3=='%'?a3*img.dimv()/100:a3,1),
  1.1643 +                x1 = (int)cimg::round(sep4=='%'?a4*img.dimx()/100:a4,1),
  1.1644 +                y1 = (int)cimg::round(sep5=='%'?a5*img.dimy()/100:a5,1),
  1.1645 +                z1 = (int)cimg::round(sep6=='%'?a6*img.dimz()/100:a6,1),
  1.1646 +                v1 = (int)cimg::round(sep7=='%'?a7*img.dimv()/100:a7,1);
  1.1647 +              gmic_apply(img,crop(x0,y0,z0,v0,x1,y1,z1,v1,borders?true:false));
  1.1648 +            }
  1.1649 +            ++position;
  1.1650 +          } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1651 +                                  "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1.1652 +                                  st0,st1,st2,st3,st4,st5,&borders,&end)==7 ||
  1.1653 +                      std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1654 +                                  "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1.1655 +                                  st0,st1,st2,st3,st4,st5,&end)==6) &&
  1.1656 +                     (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1.1657 +                      (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1.1658 +                     (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1.1659 +                      (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1.1660 +                     (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
  1.1661 +                      (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1.1662 +                     (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
  1.1663 +                      (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
  1.1664 +                     (std::sscanf(st4,"%f%c",&a4,&end)==1 ||
  1.1665 +                      (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
  1.1666 +                     (std::sscanf(st5,"%f%c",&a5,&end)==1 ||
  1.1667 +                      (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%'))) {
  1.1668 +            print("Crop image%s with (%g%s%g%s%g%s x (%g%s%g%s%g%s.",gmic_inds,
  1.1669 +                  a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",a2,sep2=='%'?"%)":")",
  1.1670 +                  a3,sep3=='%'?"%,":",",a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%)":")");
  1.1671 +            cimg_foroff(indices,l) {
  1.1672 +              CImg<T> &img = images[indices[l]];
  1.1673 +              const int
  1.1674 +                x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1.1675 +                y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1.1676 +                z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
  1.1677 +                x1 = (int)cimg::round(sep3=='%'?a3*img.dimx()/100:a3,1),
  1.1678 +                y1 = (int)cimg::round(sep4=='%'?a4*img.dimy()/100:a4,1),
  1.1679 +                z1 = (int)cimg::round(sep5=='%'?a5*img.dimz()/100:a5,1);
  1.1680 +              gmic_apply(img,crop(x0,y0,z0,x1,y1,z1,borders?true:false));
  1.1681 +            }
  1.1682 +            ++position;
  1.1683 +          } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1684 +                                  "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1.1685 +                                  st0,st1,st2,st3,&borders,&end)==5 ||
  1.1686 +                      std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1.1687 +                                  "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1.1688 +                                  st0,st1,st2,st3,&end)==4) &&
  1.1689 +                     (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1.1690 +                      (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1.1691 +                     (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1.1692 +                      (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1.1693 +                     (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
  1.1694 +                      (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1.1695 +                     (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
  1.1696 +                      (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%'))) {
  1.1697 +            print("Crop image%s with (%g%s%g%s x (%g%s%g%s.",gmic_inds,
  1.1698 +                  a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%)":")",
  1.1699 +                  a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")");
  1.1700 +            cimg_foroff(indices,l) {
  1.1701 +              CImg<T> &img = images[indices[l]];
  1.1702 +              const int
  1.1703 +                x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1.1704 +                y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1.1705 +                x1 = (int)cimg::round(sep2=='%'?a2*img.dimx()/100:a2,1),
  1.1706 +                y1 = (int)cimg::round(sep3=='%'?a3*img.dimy()/100:a3,1);
  1.1707 +              gmic_apply(img,crop(x0,y0,x1,y1,borders?true:false));
  1.1708 +            }
  1.1709 +            ++position;
  1.1710 +          } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",st0,st1,&borders,&end)==3 ||
  1.1711 +                      std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",st0,st1,&end)==2) &&
  1.1712 +                     (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1.1713 +                      (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1.1714 +                     (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1.1715 +                      (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%'))) {
  1.1716 +            print("Crop image%s with (%g%s x (%g%s.",gmic_inds,
  1.1717 +                  a0,sep0=='%'?"%)":")",a1,sep1=='%'?"%)":")");
  1.1718 +            cimg_foroff(indices,l) {
  1.1719 +              CImg<T> &img = images[indices[l]];
  1.1720 +              const int
  1.1721 +                x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1.1722 +                x1 = (int)cimg::round(sep1=='%'?a1*img.dimx()/100:a1,1);
  1.1723 +              gmic_apply(img,crop(x0,x1,borders?true:false));
  1.1724 +            }
  1.1725 +            ++position;
  1.1726 +          } else {
  1.1727 +            print("Crop image%s : Interactive mode.",gmic_inds);
  1.1728 +            char title[4096] = { 0 };
  1.1729 +            cimg_foroff(indices,l) {
  1.1730 +              CImg<T>& img = images[indices[l]];
  1.1731 +              CImgDisplay disp(cimg_fitscreen(img.dimx(),img.dimy(),1),0,1);
  1.1732 +              std::sprintf(title,"%s : Interactive crop",filenames[indices[l]].ptr());
  1.1733 +              disp.set_title(title);
  1.1734 +              const CImg<int> s = img.get_select(disp,2);
  1.1735 +              print("Crop image [%d] with (%d,%d,%d) x (%d,%d,%d).",
  1.1736 +                    indices[l],s[0],s[1],s[2],s[3],s[4],s[5]);
  1.1737 +              gmic_apply(img,crop(s[0],s[1],s[2],s[3],s[4],s[5]));
  1.1738 +            }
  1.1739 +          }
  1.1740 +          continue;
  1.1741 +        }
  1.1742 +
  1.1743 +        // Autocrop.
  1.1744 +        if (!cimg::strcmp("-autocrop",item0)) {
  1.1745 +          print("Autocrop image%s with color '%s'.",gmic_inds,argument_text);
  1.1746 +          cimg_foroff(indices,l) {
  1.1747 +            CImg<T>& img = images[indices[l]];
  1.1748 +            const CImg<T> col = CImg<T>(img.dimv()).fill(argument,true);
  1.1749 +            gmic_apply(img,autocrop(col));
  1.1750 +          }
  1.1751 +          ++position; continue;
  1.1752 +        }
  1.1753 +
  1.1754 +        // Select channels.
  1.1755 +        if (!cimg::strcmp("-channels",item0)) {
  1.1756 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1757 +          float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1758 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1759 +              ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1760 +               (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1761 +               std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1.1762 +              ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1763 +               (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1764 +               std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1.1765 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channels of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
  1.1766 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Keep channels of image%s"); value1 = images[ind1].dimv()-1.0f; sep1 = 0; }
  1.1767 +            print("Keep channels %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1.1768 +            cimg_foroff(indices,l) {
  1.1769 +              CImg<T> &img = images[indices[l]];
  1.1770 +              const int
  1.1771 +                nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1),
  1.1772 +                nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,1);
  1.1773 +              gmic_apply(img,channels(nvalue0,nvalue1));
  1.1774 +            }
  1.1775 +          } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1.1776 +                     ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1777 +                      (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1778 +                      std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1.1779 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channel of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
  1.1780 +            print("Keep channel %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1.1781 +            cimg_foroff(indices,l) {
  1.1782 +              CImg<T> &img = images[indices[l]];
  1.1783 +              const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1);
  1.1784 +              gmic_apply(img,channel(nvalue0));
  1.1785 +            }
  1.1786 +          } else error("Keep channels of image%s : Invalid argument '%s' "
  1.1787 +                       "(should be 'channel0[%%][,channel1[%%]]').",gmic_inds,argument_text);
  1.1788 +          ++position; continue;
  1.1789 +        }
  1.1790 +
  1.1791 +        // Select slices.
  1.1792 +        if (!cimg::strcmp("-slices",item0)) {
  1.1793 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1794 +          float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1795 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1796 +              ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1797 +               (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1798 +               std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1.1799 +              ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1800 +               (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1801 +               std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1.1802 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slices of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
  1.1803 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Select slices of image%s"); value1 = images[ind1].dimz()-1.0f; sep1 = 0; }
  1.1804 +            print("Select slices %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1.1805 +            cimg_foroff(indices,l) {
  1.1806 +              CImg<T> &img = images[indices[l]];
  1.1807 +              const int
  1.1808 +                nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,0),
  1.1809 +                nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,0);
  1.1810 +              gmic_apply(img,slices(nvalue0,nvalue1));
  1.1811 +            }
  1.1812 +          } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1.1813 +                     ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1814 +                      (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1815 +                      std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1.1816 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slice of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
  1.1817 +            print("Select slice %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1.1818 +            cimg_foroff(indices,l) {
  1.1819 +              CImg<T> &img = images[indices[l]];
  1.1820 +              const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimz()-1)/100:value0,1);
  1.1821 +              gmic_apply(img,slice(nvalue0));
  1.1822 +            }
  1.1823 +          } else error("Select slices of image%s : Invalid argument '%s' "
  1.1824 +                       "(should be 'slice0[%%][,slice1[%%]]').",gmic_inds,argument_text);
  1.1825 +          ++position; continue;
  1.1826 +        }
  1.1827 +
  1.1828 +        // Select lines.
  1.1829 +        if (!cimg::strcmp("-lines",item0) || !cimg::strcmp("-l",item0)) {
  1.1830 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1831 +          float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1832 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1833 +              ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1834 +               (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1835 +               std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1.1836 +              ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1837 +               (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1838 +               std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1.1839 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
  1.1840 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Select lines of image%s"); value1 = images[ind1].dimy()-1.0f; sep1 = 0; }
  1.1841 +            print("Select lines %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1.1842 +            cimg_foroff(indices,l) {
  1.1843 +              CImg<T> &img = images[indices[l]];
  1.1844 +              const int
  1.1845 +                nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1),
  1.1846 +                nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimy()-1)/100:value1,1);
  1.1847 +              gmic_apply(img,lines(nvalue0,nvalue1));
  1.1848 +            }
  1.1849 +          } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1.1850 +                     ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1851 +                      (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1852 +                      std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1.1853 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
  1.1854 +            print("Select lines %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1.1855 +            cimg_foroff(indices,l) {
  1.1856 +              CImg<T> &img = images[indices[l]];
  1.1857 +              const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1);
  1.1858 +              gmic_apply(img,line(nvalue0));
  1.1859 +            }
  1.1860 +          } else error("Select lines of image%s : Invalid argument '%s' "
  1.1861 +                       "(should be 'line0[%%][,line1[%%]]').",gmic_inds,argument_text);
  1.1862 +          ++position; continue;
  1.1863 +        }
  1.1864 +
  1.1865 +        // Columns.
  1.1866 +        if (!cimg::strcmp("-columns",item0)) {
  1.1867 +          char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1.1868 +          float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1.1869 +          if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1.1870 +              ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1871 +               (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1872 +               std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1.1873 +              ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1.1874 +               (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1.1875 +               std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1.1876 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
  1.1877 +            if (ind1!=no_ind) { gmic_check_indice(ind1,"Select columns of image%s"); value1 = images[ind1].dimx()-1.0f; sep1 = 0; }
  1.1878 +            print("Select columns %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1.1879 +            cimg_foroff(indices,l) {
  1.1880 +              CImg<T> &img = images[indices[l]];
  1.1881 +              const int
  1.1882 +                nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1),
  1.1883 +                nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimx()-1)/100:value1,1);
  1.1884 +              gmic_apply(img,lines(nvalue0,nvalue1));
  1.1885 +            }
  1.1886 +          } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1.1887 +                     ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1.1888 +                      (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.1889 +                      std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1.1890 +            if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
  1.1891 +            print("Select columns %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1.1892 +            cimg_foroff(indices,l) {
  1.1893 +              CImg<T> &img = images[indices[l]];
  1.1894 +              const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1);
  1.1895 +              gmic_apply(img,line(nvalue0));
  1.1896 +            }
  1.1897 +          } else error("Select columns of image%s : Invalid argument '%s' "
  1.1898 +                       "(should be 'column0[%%][,column1[%%]]').",gmic_inds,argument_text);
  1.1899 +          ++position; continue;
  1.1900 +        }
  1.1901 +
  1.1902 +        // Rotate.
  1.1903 +        if (!cimg::strcmp("-rotate",item0)) {
  1.1904 +          float angle = 0; int borders = 0, interpolation = 1; char end = 0;
  1.1905 +          if (std::sscanf(argument,"%f%c",&angle,&end)==1 ||
  1.1906 +              std::sscanf(argument,"%f%*c%d%c",&angle,&borders,&end)==2 ||
  1.1907 +              std::sscanf(argument,"%f%*c%d%*c%d%c",&angle,&borders,&interpolation,&end)==3) {
  1.1908 +            print("Rotate image%s with an angle of %g deg and %s interpolation.",
  1.1909 +                  gmic_inds,angle,interpolation?"linear":"nearest-neighbor");
  1.1910 +            if (borders>=0) { cimg_foroff(indices,l) gmic_apply(images[indices[l]],rotate(angle,borders,interpolation)); }
  1.1911 +            else cimg_foroff(indices,l) {
  1.1912 +              CImg<T> &img = images[indices[l]];
  1.1913 +              gmic_apply(img,rotate(angle,img.dimx()/2.0f,img.dimy()/2.0f,1,-1-borders,interpolation));
  1.1914 +            }
  1.1915 +          } else error("Rotate image%s : Invalid argument '%s' "
  1.1916 +                       "(should be 'angle[,border_conditions[,interpolation]]').",gmic_inds,argument_text);
  1.1917 +          ++position;
  1.1918 +          continue;
  1.1919 +        }
  1.1920 +
  1.1921 +        // Mirror.
  1.1922 +        if (!cimg::strcmp("-mirror",item0)) {
  1.1923 +          const char axis = cimg::uncase(*argument);
  1.1924 +          if (cimg::strlen(argument)==1) {
  1.1925 +            print("Mirror image%s along the %c-axis.",gmic_inds,axis);
  1.1926 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],mirror(axis));
  1.1927 +          } else error("Mirror image%s : Invalid argument '%s' "
  1.1928 +                       "(should be '{x,y,z,v}').",gmic_inds,argument_text);
  1.1929 +          ++position; continue;
  1.1930 +        }
  1.1931 +
  1.1932 +        // Translate.
  1.1933 +        if (!cimg::strcmp("-translate",item0)) {
  1.1934 +          char stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
  1.1935 +          char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
  1.1936 +          float dx = 0, dy = 0, dz = 0, dv = 0; int borders = 0;
  1.1937 +          if (((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1.1938 +                            stx,sty,stz,stv,&borders,&end)==5 ||
  1.1939 +                std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1.1940 +                            stx,sty,stz,stv,&end)==4) &&
  1.1941 +               (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1.1942 +               (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
  1.1943 +               (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%')) &&
  1.1944 +               (std::sscanf(stv,"%f%c",&dv,&end)==1 || (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%'))) ||
  1.1945 +              (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
  1.1946 +               (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1.1947 +               (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
  1.1948 +               (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%'))) ||
  1.1949 +              (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,&end)==2 &&
  1.1950 +               (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1.1951 +               (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%'))) ||
  1.1952 +              (std::sscanf(argument,"%4095[0-9.eE%+-]%c",stx,&end)==1 &&
  1.1953 +               (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')))) {
  1.1954 +            print("Translate image%s with vector (%g%s,%g%s,%g%s,%g%s).",
  1.1955 +                  gmic_inds,dx,sepx=='%'?"%":"",dy,sepy=='%'?"%":"",dz,sepz=='%'?"%":"",dv,sepv=='%'?"%":"");
  1.1956 +            cimg_foroff(indices,l) {
  1.1957 +              CImg<T> &img = images[indices[l]];
  1.1958 +              const int
  1.1959 +                ndx = (int)cimg::round(sepx=='%'?dx*img.dimx()/100:dx,1),
  1.1960 +                ndy = (int)cimg::round(sepy=='%'?dy*img.dimy()/100:dy,1),
  1.1961 +                ndz = (int)cimg::round(sepz=='%'?dz*img.dimz()/100:dz,1),
  1.1962 +                ndv = (int)cimg::round(sepv=='%'?dv*img.dimv()/100:dv,1);
  1.1963 +              gmic_apply(images[indices[l]],translate(ndx,ndy,ndz,ndv,borders));
  1.1964 +            }
  1.1965 +          } else error("Translate image%s : Invalid argument '%s' "
  1.1966 +                       "(should be 'tx[%%][,ty[%%][,tz[%%][,tv[%%][,border_conditions]]]]').",gmic_inds,argument_text);
  1.1967 +          ++position; continue;
  1.1968 +        }
  1.1969 +
  1.1970 +        // Transpose.
  1.1971 +        gmic_simple_item("-transpose",transpose,"Transpose image%s.");
  1.1972 +
  1.1973 +        // Invert.
  1.1974 +        gmic_simple_item("-invert",invert,"Compute matrix inversion of image%s.");
  1.1975 +
  1.1976 +        // Permute axes.
  1.1977 +        if (!cimg::strcmp("-permute",item0)) {
  1.1978 +          print("Permute axes of image%s with permutation '%s'.",gmic_inds,argument_text);
  1.1979 +          cimg_foroff(indices,l) gmic_apply(images[indices[l]],permute_axes(argument));
  1.1980 +          ++position; continue;
  1.1981 +        }
  1.1982 +
  1.1983 +        // Unroll.
  1.1984 +        if (!cimg::strcmp("-unroll",item0)) {
  1.1985 +          const char axis = cimg::uncase(*argument);
  1.1986 +          if (cimg::strlen(argument)==1 && (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
  1.1987 +            print("Unroll image%s along the %c-axis.",gmic_inds,axis);
  1.1988 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],unroll(axis));
  1.1989 +          } else error("Unroll image%s : Invalid argument '%s' "
  1.1990 +                       "(should be '{x,y,z,v}').",gmic_inds,argument_text);
  1.1991 +          ++position; continue;
  1.1992 +        }
  1.1993 +
  1.1994 +        // Split image(s).
  1.1995 +        if (!cimg::strcmp("-split",item0) || !cimg::strcmp("-s",item0)) {
  1.1996 +          char axis = cimg::uncase(*argument), foo = 0, end = 0; int nb = 0, keep_value = 0; double value = 0;
  1.1997 +          if ((std::sscanf(argument,"%c%c",&foo,&end)==1 ||
  1.1998 +               std::sscanf(argument,"%c%*c%d%c",&foo,&nb,&end)==2) &
  1.1999 +              (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
  1.2000 +            if (nb<0) error("Split image%s along the %c-axis in %d part : Invalid number of parts.",
  1.2001 +                            gmic_inds,axis,nb);
  1.2002 +            if (nb>0) print("Split image%s along the %c-axis in %d parts.",gmic_inds,axis,nb);
  1.2003 +            else print("Split image%s along the %c-axis.",gmic_inds,axis);
  1.2004 +            unsigned int off = 0;
  1.2005 +            cimg_foroff(indices,l) {
  1.2006 +              const unsigned int ind = indices[l] + off;
  1.2007 +              const CImg<T>& img = images[ind];
  1.2008 +              const CImg<char> filename = filenames[ind];
  1.2009 +              const CImgList<T> split = img.get_split(axis,nb);
  1.2010 +              if (get_version) {
  1.2011 +                images.insert(split);
  1.2012 +                filenames.insert(split.size,filename);
  1.2013 +              } else {
  1.2014 +                images.remove(ind); images.insert(split,ind);
  1.2015 +                filenames.remove(ind); filenames.insert(split.size,filename,ind);
  1.2016 +                off+=split.size-1;
  1.2017 +              }
  1.2018 +            }
  1.2019 +          } else if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1.2020 +                     std::sscanf(argument,"%lf%*c%d%c",&value,&keep_value,&end)==2) {
  1.2021 +            print("Split image%s according to value %g.",gmic_inds,value);
  1.2022 +            unsigned int off = 0;
  1.2023 +            cimg_foroff(indices,l) {
  1.2024 +              const unsigned int ind = indices[l] + off;
  1.2025 +              CImg<T>& img = images[ind];
  1.2026 +              const CImg<char> filename = filenames[ind];
  1.2027 +              const CImgList<T> split = img.get_split((T)value,keep_value,false);
  1.2028 +              if (get_version) {
  1.2029 +                images.insert(split);
  1.2030 +                filenames.insert(split.size,filename);
  1.2031 +              } else {
  1.2032 +                images.remove(ind); images.insert(split,ind);
  1.2033 +                filenames.remove(ind); filenames.insert(split.size,filename,ind);
  1.2034 +                off+=split.size-1;
  1.2035 +              }
  1.2036 +            }
  1.2037 +          } else error("Split image%s : Invalid argument '%s' "
  1.2038 +                       "(should be 'axis[,nb_parts]' where 'axis' can be '{x,y,z,v}').",gmic_inds,argument_text);
  1.2039 +          ++position; continue;
  1.2040 +        }
  1.2041 +
  1.2042 +        // Append image(s).
  1.2043 +        if (!cimg::strcmp("-append",item0) || !cimg::strcmp("-a",item0)) {
  1.2044 +          char axis = 0, align='p', end = 0;
  1.2045 +          if ((std::sscanf(argument,"%c%c",&axis,&end)==1 ||
  1.2046 +               std::sscanf(argument,"%c%*c%c%c",&axis,&align,&end)==2)) {
  1.2047 +            axis = cimg::uncase(axis);
  1.2048 +            print("Append image%s along the %c-axis with %s alignment.",
  1.2049 +                  gmic_inds,axis,align=='p'?"left":align=='c'?"center":"right");
  1.2050 +            CImgList<T> subimages; cimg_foroff(indices,l) subimages.insert(images[indices[l]],~0U,true);
  1.2051 +            if (get_version) {
  1.2052 +              images.insert(subimages.get_append(axis,align));
  1.2053 +              filenames.insert(filenames[indices[0]]);
  1.2054 +            } else {
  1.2055 +              images.insert(subimages.get_append(axis,align),indices[0]);
  1.2056 +              filenames.insert(filenames[indices[0]],indices[0]);
  1.2057 +              int off = 1;
  1.2058 +              cimg_foroff(indices,l) {
  1.2059 +                const int ind = indices[l] + off;
  1.2060 +                images.remove(ind); filenames.remove(ind);
  1.2061 +                --off;
  1.2062 +              }
  1.2063 +            }
  1.2064 +          } else error("Append image%s : Invalid argument '%s' "
  1.2065 +                       "(should be 'axis[,alignement]' where 'axis' can be '{x,y,z,v}' "
  1.2066 +                       "and alignement '{p,c,n}').",gmic_inds,argument_text);
  1.2067 +          ++position; continue;
  1.2068 +        }
  1.2069 +
  1.2070 +        // Warp image(s).
  1.2071 +        if (!cimg::strcmp("-warp",item0)) {
  1.2072 +          int ind0 = no_ind, interpolation = 1, relative = 0, nb = 1, borders = 1; char end = 0, sep = 0;
  1.2073 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')||
  1.2074 +              std::sscanf(argument,"[%d]%*c%d%c",&ind0,&relative,&end)==2 ||
  1.2075 +              std::sscanf(argument,"[%d]%*c%d%*c%d%c",&ind0,&relative,&interpolation,&end)==3 ||
  1.2076 +              std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&end)==4 ||
  1.2077 +              std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&nb,&end)==5) {
  1.2078 +            gmic_check_indice(ind0,"Warp image%s");
  1.2079 +            if (nb!=1) print("Warp image%s with %s field [%u] and %d frames.",
  1.2080 +                             gmic_inds,relative?"relative":"absolute",ind0,nb);
  1.2081 +            else print("Warp image%s with %s field [%u].",gmic_inds,relative?"relative":"absolute",ind0);
  1.2082 +            if (nb>=1) {
  1.2083 +              const CImg<T> warp = images[ind0];
  1.2084 +              unsigned int off = 0;
  1.2085 +              cimg_foroff(indices,l) {
  1.2086 +                const unsigned int ind = indices[l] + off;
  1.2087 +                CImg<T> &img = images[ind];
  1.2088 +                CImgList<T> frames(nb);
  1.2089 +                cimglist_for(frames,t) {
  1.2090 +                  const CImg<T> nwarp = warp.get_resize(img.dimx(),img.dimy(),img.dimz(),warp.dimv(),3)*=(t+1.0f)/nb;
  1.2091 +                  frames[t] = img.get_warp(nwarp,relative?true:false,interpolation?true:false,borders);
  1.2092 +                }
  1.2093 +                if (get_version) {
  1.2094 +                  images.insert(frames);
  1.2095 +                  filenames.insert(nb-1,filenames[ind]);
  1.2096 +                } else {
  1.2097 +                  images.remove(ind); images.insert(frames,ind);
  1.2098 +                  filenames.insert(nb-1,filenames[ind],ind);
  1.2099 +                  off+=nb-1;
  1.2100 +                }
  1.2101 +              }
  1.2102 +            }
  1.2103 +          } else error("Warp image%s : Invalid argument '%s' "
  1.2104 +                       "(should be '[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]').",
  1.2105 +                       gmic_inds,argument_text);
  1.2106 +          ++position; continue;
  1.2107 +        }
  1.2108 +
  1.2109 +        //-----------------------
  1.2110 +        // Image filtering
  1.2111 +        //-----------------------
  1.2112 +
  1.2113 +        // Gaussian blur.
  1.2114 +        if (!cimg::strcmp("-blur",item0)) {
  1.2115 +          float sigma = -1; int borders = 1; char end = 0;
  1.2116 +          if ((std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
  1.2117 +               std::sscanf(argument,"%f%*c%d%c",&sigma,&borders,&end)==2)
  1.2118 +              && sigma>=0) {
  1.2119 +            print("Blur image%s with standard deviation %g.",gmic_inds,sigma);
  1.2120 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur(sigma,borders?true:false));
  1.2121 +          } else error("Blur image%s : Invalid argument '%s' "
  1.2122 +                       "(should be 'stdev[,border_conditions]', with stdev>=0).",gmic_inds,argument_text);
  1.2123 +          ++position; continue;
  1.2124 +        }
  1.2125 +
  1.2126 +        // Bilateral filter.
  1.2127 +        if (!cimg::strcmp("-bilateral",item0)) {
  1.2128 +          float sigmas = 0, sigmar = 0; char end = 0;
  1.2129 +          if (std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2) {
  1.2130 +            print("Apply bilateral filter on image%s with standart deviations %g and %g.",
  1.2131 +                  gmic_inds,sigmas,sigmar);
  1.2132 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_bilateral(sigmas,sigmar));
  1.2133 +          } else error("Apply bilateral filter on image%s : Invalid argument '%s' "
  1.2134 +                       "(should be 'stdevs,stdevr').",gmic_inds,argument_text);
  1.2135 +          ++position; continue;
  1.2136 +        }
  1.2137 +
  1.2138 +        // Smooth.
  1.2139 +        if (!cimg::strcmp("-smooth",item0)) {
  1.2140 +          float amplitude = 0, sharpness = 0.7f, anisotropy = 0.3f, alpha = 0.6f, sigma = 1.1f, dl =0.8f, da = 30.0f, gauss_prec = 2.0f;
  1.2141 +          unsigned int interpolation_type = 0, fast_approx = 1;
  1.2142 +          char end = 0;
  1.2143 +          if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
  1.2144 +              std::sscanf(argument,"%f%*c%f%c",&amplitude,&sharpness,&end)==2 ||
  1.2145 +              std::sscanf(argument,"%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&end)==3 ||
  1.2146 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&end)==4 ||
  1.2147 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&end)==5 ||
  1.2148 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&end)==6 ||
  1.2149 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&end)==7 ||
  1.2150 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",
  1.2151 +                          &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&end)==8 ||
  1.2152 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%c",
  1.2153 +                          &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&end)==9 ||
  1.2154 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%*c%u%c",
  1.2155 +                          &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&fast_approx,&end)==10) {
  1.2156 +            print("Smooth image%s anisotropically with "
  1.2157 +                  "amplitude %g, sharpness %g, anisotropy %g, alpha %g and sigma %g.",
  1.2158 +                  gmic_inds,amplitude,sharpness,anisotropy,alpha,sigma);
  1.2159 +            cimg_foroff(indices,l)
  1.2160 +              gmic_apply(images[indices[l]],blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,
  1.2161 +                                                             dl,da,gauss_prec,interpolation_type,fast_approx?true:false));
  1.2162 +          } else error("Smooth image%s anisotropically : Invalid argument '%s' "
  1.2163 +                       "(should be 'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]').",
  1.2164 +                       gmic_inds,argument_text);
  1.2165 +          ++position; continue;
  1.2166 +        }
  1.2167 +
  1.2168 +        // Patch averaging.
  1.2169 +        if (!cimg::strcmp("-denoise",item0)) {
  1.2170 +          float sigmas = 10, sigmar = 10; int psize = 5, rsize = 6; char end = 0;
  1.2171 +          if (std::sscanf(argument,"%f%c",&sigmas,&end)==1 ||
  1.2172 +              std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2 ||
  1.2173 +              std::sscanf(argument,"%f%*c%f%*c%d%c",&sigmas,&sigmar,&psize,&end)==3 ||
  1.2174 +              std::sscanf(argument,"%f%*c%f%*c%d%*c%d%c",&sigmas,&sigmar,&psize,&rsize,&end)==4) {
  1.2175 +            if (sigmas<0 || sigmar<0 || psize<0 || rsize<0)
  1.2176 +              error("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d : "
  1.2177 +                    "Invalid parameters.",gmic_inds,psize,psize,sigmas,sigmar,rsize);
  1.2178 +            print("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d.",
  1.2179 +                  gmic_inds,psize,psize,sigmas,sigmar,rsize);
  1.2180 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_patch(psize,sigmas,sigmar,rsize));
  1.2181 +          } else error("Denoise image%s : Invalid argument '%s' "
  1.2182 +                       "(should be 'stdev_s[,stdev_p[,patch_size[,lookup_size]]]').",
  1.2183 +                       gmic_inds,argument_text);
  1.2184 +          ++position; continue;
  1.2185 +        }
  1.2186 +
  1.2187 +        // Median filter.
  1.2188 +        if (!cimg::strcmp("-median",item0)) {
  1.2189 +          int siz = 3; char end = 0;
  1.2190 +          if (std::sscanf(argument,"%d%c",&siz,&end)==1) {
  1.2191 +            if (siz<=0) error("Apply median filter on image%s : Invalid size %d.",gmic_inds,siz);
  1.2192 +            print("Apply median filter of size %d on image%s.",siz,gmic_inds);
  1.2193 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_median(siz));
  1.2194 +          } else error("Apply median filter on image%s : Invalid argument '%s' "
  1.2195 +                       "(should be 'size').",gmic_inds,argument_text);
  1.2196 +          ++position; continue;
  1.2197 +        }
  1.2198 +
  1.2199 +        // Sharpen.
  1.2200 +        if (!cimg::strcmp("-sharpen",item0)) {
  1.2201 +          float amplitude = 0, edge = 1, alpha = 0, sigma = 0; int sharpen_type = 0; char end = 0;
  1.2202 +          if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
  1.2203 +              std::sscanf(argument,"%f%*c%d%c",&amplitude,&sharpen_type,&end)==2 ||
  1.2204 +              std::sscanf(argument,"%f%*c%d%*c%f%c",&amplitude,&sharpen_type,&edge,&end)==3 ||
  1.2205 +              std::sscanf(argument,"%f%*c%d%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&end)==4 ||
  1.2206 +              std::sscanf(argument,"%f%*c%d%*c%f%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&sigma,&end)==5) {
  1.2207 +            if (sharpen_type)
  1.2208 +              print("Sharpen image%s with shock filters and amplitude %g, edge %g, alpha %g and sigma %g.",
  1.2209 +                    gmic_inds,amplitude,edge,alpha,sigma);
  1.2210 +            else
  1.2211 +              print("Sharpen image%s with inverse diffusion and amplitude %g.",gmic_inds,amplitude);
  1.2212 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],sharpen(amplitude,sharpen_type?true:false,edge,alpha,sigma));
  1.2213 +          } else error("Sharpen image%s : Invalid argument '%s' "
  1.2214 +                       "(should be 'amplitude[,sharpen_type[,edge[,alpha[,sigma]]]]', "
  1.2215 +                       "where 'sharpen_type' can be '{0=inverse diffusion, 1=shock filters}').",
  1.2216 +                       gmic_inds,argument_text);
  1.2217 +          ++position; continue;
  1.2218 +        }
  1.2219 +
  1.2220 +        // Convolve.
  1.2221 +        if (!cimg::strcmp("-convolve",item0)) {
  1.2222 +          int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  1.2223 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2224 +              std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  1.2225 +            gmic_check_indice(ind0,"Convolve image%s");
  1.2226 +            print("Convolve image%s with mask [%d].",gmic_inds,ind0);
  1.2227 +            const CImg<T> mask = images[ind0];
  1.2228 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],convolve(mask,borders));
  1.2229 +          } else error("Convolve image%s : Invalid argument '%s' "
  1.2230 +                       "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
  1.2231 +          ++position; continue;
  1.2232 +        }
  1.2233 +
  1.2234 +        // Correlate.
  1.2235 +        if (!cimg::strcmp("-correlate",item0)) {
  1.2236 +          int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  1.2237 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2238 +              std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  1.2239 +            gmic_check_indice(ind0,"Correlate image%s");
  1.2240 +            print("Correlate image%s with mask [%d].",gmic_inds,ind0);
  1.2241 +            const CImg<T> mask = images[ind0];
  1.2242 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],correlate(mask,borders));
  1.2243 +          } else error("Correlate image%s : Invalid argument '%s' "
  1.2244 +                       "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
  1.2245 +          ++position; continue;
  1.2246 +        }
  1.2247 +
  1.2248 +        // Erode.
  1.2249 +        if (!cimg::strcmp("-erode",item0)) {
  1.2250 +          int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  1.2251 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2252 +              std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  1.2253 +            gmic_check_indice(ind0,"Erode image%s");
  1.2254 +            print("Erode image%s with mask [%d].",gmic_inds,ind0);
  1.2255 +            const CImg<T> mask = images[ind0];
  1.2256 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(mask,borders));
  1.2257 +          } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
  1.2258 +                     std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
  1.2259 +            if (siz<=0) error("Erode image%s : Invalid size %d.",gmic_inds,siz);
  1.2260 +            print("Erode image%s with size %d.",gmic_inds,siz);
  1.2261 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(siz,borders));
  1.2262 +          } else error("Erode image%s : Invalid argument '%s' "
  1.2263 +                       "(should be '[indice]' or 'size').",gmic_inds,argument_text);
  1.2264 +          ++position; continue;
  1.2265 +        }
  1.2266 +
  1.2267 +        // Dilate.
  1.2268 +        if (!cimg::strcmp("-dilate",item0)) {
  1.2269 +          int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  1.2270 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2271 +              std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  1.2272 +            gmic_check_indice(ind0,"Dilate image%s");
  1.2273 +            print("Dilate image%s with mask [%d].",gmic_inds,ind0);
  1.2274 +            const CImg<T> mask = images[ind0];
  1.2275 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(mask,borders));
  1.2276 +          } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
  1.2277 +                     std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
  1.2278 +            if (siz<=0) error("Dilate image%s : Invalid size %d.",gmic_inds,siz);
  1.2279 +            print("Dilate image%s with size %d.",gmic_inds,siz);
  1.2280 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(siz,borders));
  1.2281 +          } else error("Dilate image%s : Invalid argument '%s' "
  1.2282 +                       "(should be '[indice]' or 'size').",gmic_inds,argument_text);
  1.2283 +          ++position; continue;
  1.2284 +        }
  1.2285 +
  1.2286 +        // Compute gradient.
  1.2287 +        if (!cimg::strcmp("-gradient",item0)) {
  1.2288 +          char axes[4096] = { 0 }, *naxes = 0, end = 0; int scheme = 3;
  1.2289 +          print("Compute gradient of image%s.",gmic_inds);
  1.2290 +          if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1 ||
  1.2291 +              std::sscanf(argument,"%4095[xyz]%*c%d%c",axes,&scheme,&end)==2) { naxes = axes; ++position; }
  1.2292 +          unsigned int off = 0;
  1.2293 +          cimg_foroff(indices,l) {
  1.2294 +            const unsigned int ind = indices[l] + off;
  1.2295 +            CImg<T>& img = images[ind];
  1.2296 +            const CImg<char> filename = filenames[ind];
  1.2297 +            const CImgList<T> gradient = img.get_gradient(naxes,scheme);
  1.2298 +            if (get_version) {
  1.2299 +              images.insert(gradient);
  1.2300 +              filenames.insert(gradient.size,filename);
  1.2301 +            } else {
  1.2302 +              images.remove(ind); images.insert(gradient,ind);
  1.2303 +              filenames.remove(ind); filenames.insert(gradient.size,filename,ind);
  1.2304 +              off+=gradient.size-1;
  1.2305 +            }
  1.2306 +          }
  1.2307 +          continue;
  1.2308 +        }
  1.2309 +
  1.2310 +        // Compute Hessian.
  1.2311 +        if (!cimg::strcmp("-hessian",item0)) {
  1.2312 +          char axes[4096] = { 0 }, *naxes = 0, end = 0;
  1.2313 +          print("Compute Hessian of image%s.",gmic_inds);
  1.2314 +          if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1) { naxes = axes; ++position; }
  1.2315 +          unsigned int off = 0;
  1.2316 +          cimg_foroff(indices,l) {
  1.2317 +            const unsigned int ind = indices[l] + off;
  1.2318 +            CImg<T>& img = images[ind];
  1.2319 +            const CImg<char> filename = filenames[ind];
  1.2320 +            const CImgList<T> hessian = img.get_hessian(naxes);
  1.2321 +            if (get_version) {
  1.2322 +              images.insert(hessian);
  1.2323 +              filenames.insert(hessian.size,filename);
  1.2324 +            } else {
  1.2325 +              images.remove(ind); images.insert(hessian,ind);
  1.2326 +              filenames.remove(ind); filenames.insert(hessian.size,filename,ind);
  1.2327 +              off+=hessian.size-1;
  1.2328 +            }
  1.2329 +          }
  1.2330 +          continue;
  1.2331 +        }
  1.2332 +
  1.2333 +        // Compute direct or inverse FFT.
  1.2334 +        const bool inv_fft = !cimg::strcmp("-ifft",item0);
  1.2335 +        if (!cimg::strcmp("-fft",item0) || inv_fft) {
  1.2336 +          print("Compute %sFourier Transform of complex data",inv_fft?"inverse ":"");
  1.2337 +          cimg_foroff(indices,l) {
  1.2338 +            const unsigned int ind0 = indices[l], ind1 = l+1<_maxl?indices[l+1]:~0U;
  1.2339 +            if (ind1!=~0U) {
  1.2340 +              if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],[%u])%c",ind0,ind1,l==_maxl-1?'.':',');
  1.2341 +              CImgList<T> fft(images[ind0],images[ind1],!get_version);
  1.2342 +              fft.FFT(inv_fft);
  1.2343 +              if (get_version) {
  1.2344 +                images.insert(2);
  1.2345 +                fft[0].transfer_to(images[images.size-2]);
  1.2346 +                fft[1].transfer_to(images[images.size-1]);
  1.2347 +                filenames.insert(filenames[ind0]);
  1.2348 +                filenames.insert(filenames[ind1]);
  1.2349 +              } else {
  1.2350 +                fft[0].transfer_to(images[ind0]);
  1.2351 +                fft[1].transfer_to(images[ind1]);
  1.2352 +              }
  1.2353 +              ++l;
  1.2354 +            } else {
  1.2355 +              if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],0)",ind0);
  1.2356 +              CImgList<T> fft(images[ind0],!get_version);
  1.2357 +              fft.insert(fft[0],~0U,false);
  1.2358 +              fft[1].fill(0);
  1.2359 +              fft.FFT(inv_fft);
  1.2360 +              if (get_version) {
  1.2361 +                images.insert(2);
  1.2362 +                fft[0].transfer_to(images[images.size-2]);
  1.2363 +                fft[1].transfer_to(images[images.size-1]);
  1.2364 +                filenames.insert(2,filenames[ind0]);
  1.2365 +              } else {
  1.2366 +                fft[0].transfer_to(images[ind0]);
  1.2367 +                images.insert(fft[1],1+ind0);
  1.2368 +                filenames.insert(filenames[ind0],1+ind0);
  1.2369 +              }
  1.2370 +            }
  1.2371 +          }
  1.2372 +          continue;
  1.2373 +        }
  1.2374 +
  1.2375 +        //-----------------------------
  1.2376 +        // Image creation and drawing
  1.2377 +        //-----------------------------
  1.2378 +
  1.2379 +        // Dimensions.
  1.2380 +        if (!cimg::strcmp("-dimensions",item0)) {
  1.2381 +          print("Get dimensions of image%s.",gmic_inds);
  1.2382 +          cimg_foroff(indices,l) {
  1.2383 +            CImg<T>& img = images[indices[l]];
  1.2384 +            CImg<int> dims = CImg<int>::vector(img.dimx(),img.dimy(),img.dimz(),img.dimv());
  1.2385 +            gmic_apply(img,replace(dims));
  1.2386 +          }
  1.2387 +          continue;
  1.2388 +        }
  1.2389 +
  1.2390 +        // Stats.
  1.2391 +        if (!cimg::strcmp("-stats",item0)) {
  1.2392 +          print("Get statistics of image%s.",gmic_inds);
  1.2393 +          cimg_foroff(indices,l) gmic_apply(images[indices[l]],stats());
  1.2394 +          continue;
  1.2395 +        }
  1.2396 +
  1.2397 +        // Histogram.
  1.2398 +        if (!cimg::strcmp("-histogram",item0)) {
  1.2399 +          int nb_levels = 256; char sep = 0, end = 0;
  1.2400 +          if (std::sscanf(argument,"%d%c",&nb_levels,&end)==1 ||
  1.2401 +              (std::sscanf(argument,"%d%c%c",&nb_levels,&sep,&end)==2 && sep=='%')) {
  1.2402 +            print("Compute histogram of image%s using %d%s levels.",gmic_inds,nb_levels,sep=='%'?"%":"");
  1.2403 +            cimg_foroff(indices,l) {
  1.2404 +              CImg<T> &img = images[indices[l]];
  1.2405 +              int nnb_levels = nb_levels;
  1.2406 +              if (sep=='%') { double m, M = img.maxmin(m); nnb_levels = (int)cimg::round(nb_levels*(1+M-m)/100,1); }
  1.2407 +              gmic_apply(images[indices[l]],histogram(nnb_levels));
  1.2408 +            }
  1.2409 +          } else error("Compute histogram of image%s : Invalid argument '%s' "
  1.2410 +                       "(should be 'nb_levels[%%]').",gmic_inds,argument_text);
  1.2411 +          ++position; continue;
  1.2412 +        }
  1.2413 +
  1.2414 +        // Distance function.
  1.2415 +        if (!cimg::strcmp("-distance",item0)) {
  1.2416 +          double value = 0; char sep = 0, end = 0;
  1.2417 +          if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1.2418 +              (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%')) {
  1.2419 +            print("Compute distance map of image%s to isovalue %g%s.",gmic_inds,value,sep=='%'?"%":"");
  1.2420 +            cimg_foroff(indices,l) {
  1.2421 +              CImg<T> &img = images[indices[l]];
  1.2422 +              double isovalue = value;
  1.2423 +              if (sep=='%') { double m, M = img.maxmin(m); isovalue = m + value*(M - m)/100; }
  1.2424 +              gmic_apply(img,distance((T)isovalue));
  1.2425 +            }
  1.2426 +          } else error("Compute distance function of image%s : Invalid argument '%s' "
  1.2427 +                       "(should be 'value[%%]').",gmic_inds,argument_text);
  1.2428 +          ++position; continue;
  1.2429 +        }
  1.2430 +
  1.2431 +        // Apply Hamilton-Jacobi PDE to compute distance to 0.
  1.2432 +        if (!cimg::strcmp("-hamilton",item0)) {
  1.2433 +          int nb_iter = 0; float band_size = 0; char end = 0;
  1.2434 +          if (std::sscanf(argument,"%d%c",&nb_iter,&end)==1 ||
  1.2435 +              std::sscanf(argument,"%d%*c%f%c",&nb_iter,&band_size,&end)==2) {
  1.2436 +            print("Apply %d iterations of Hamilton-Jacobi PDE on image%s.",nb_iter,gmic_inds);
  1.2437 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],distance_hamilton((unsigned int)nb_iter,band_size));
  1.2438 +          } else error("Apply %d iterations of Hamilton-Jacobi PDE on image%s : Invalid argument '%s' "
  1.2439 +                       "(should be 'nb_iter[,band_size]', with band_size>0).",nb_iter,gmic_inds,argument_text);
  1.2440 +          ++position; continue;
  1.2441 +        }
  1.2442 +
  1.2443 +        // Label regions.
  1.2444 +        gmic_simple_item("-label",label_regions,"Label regions on image%s.");
  1.2445 +
  1.2446 +        // Displacement field.
  1.2447 +        if (!cimg::strcmp("-displacement",item0)) {
  1.2448 +          float smooth = 0.1f, precision = 0.1f; int ind0 = no_ind, nbscales = 0, itermax = 1000; char sep = 0, end = 0;
  1.2449 +          if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2450 +              std::sscanf(argument,"[%d]%*c%f%c",&ind0,&smooth,&end)==2 ||
  1.2451 +              std::sscanf(argument,"[%d]%*c%f%*c%f%c",&ind0,&smooth,&precision,&end)==3 ||
  1.2452 +              std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%c",&ind0,&smooth,&precision,&nbscales,&end)==4 ||
  1.2453 +              std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%*c%d%c",&ind0,&smooth,&precision,&nbscales,&itermax,&end)==5) {
  1.2454 +            gmic_check_indice(ind0,"Compute displacement field of image%s");
  1.2455 +            print("Compute displacement field of image%s with target [%u] and smoothness %g.",
  1.2456 +                  gmic_inds,ind0,smooth);
  1.2457 +            const CImg<T> target = images[ind0];
  1.2458 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],displacement_field(target,smooth,precision,nbscales,itermax));
  1.2459 +          } else error("Compute displacement field of image%s : Invalid argument '%s' "
  1.2460 +                       "(should be '[indice][,smoothness[,precision[,nbscales[,itermax]]]]').",gmic_inds,argument_text);
  1.2461 +          ++position; continue;
  1.2462 +        }
  1.2463 +
  1.2464 +        // Sort.
  1.2465 +        gmic_simple_item("-sort",sort,"Sort values in image%s.");
  1.2466 +
  1.2467 +        // PSNR.
  1.2468 +        if (!cimg::strcmp("-psnr",item0)) {
  1.2469 +          double valmax = 255; char end = 0;
  1.2470 +          if (std::sscanf(argument,"%lf%c",&valmax,&end)==1) ++position;
  1.2471 +          if (images.size) {
  1.2472 +            const unsigned int siz = indices.size();
  1.2473 +            print("Compute a %ux%u matrix [%u] of PSNR values (max. pixel value is %g).",siz,siz,images.size,valmax);
  1.2474 +            CImg<T> res(siz,siz,1,1,(T)-1);
  1.2475 +            cimg_forXY(res,x,y) if (x>y) res(x,y) = res(y,x) = (T)images[indices[x]].PSNR(images[indices[y]],(float)valmax);
  1.2476 +            images.insert(res);
  1.2477 +            filenames.insert(CImg<char>("PSNR",5,1,1,1,false));
  1.2478 +          } else error("Compute PSNR : image list is empty.");
  1.2479 +          continue;
  1.2480 +        }
  1.2481 +
  1.2482 +        // Draw point.
  1.2483 +        if (!cimg::strcmp("-point",item0)) {
  1.2484 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
  1.2485 +          char sepx0 = 0, sepy0 = 0, sepz0 = 0, end = 0;
  1.2486 +          float x0 = 0, y0 = 0, z0 = 0, opacity = 1;
  1.2487 +          if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%4095[0-9.eE,+-]",
  1.2488 +                          arg0,arg1,arg2,&opacity,color)>=2 &&
  1.2489 +              ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  1.2490 +               std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  1.2491 +              ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  1.2492 +               std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
  1.2493 +              ((std::sscanf(arg2,"%f%c%c",&z0,&sepz0,&end)==2 && sepz0=='%') ||
  1.2494 +               std::sscanf(arg2,"%f%c",&z0,&end)==1 || !arg2[0])) {
  1.2495 +            print("Draw point (%g%s,%g%s,%g%s) with color '%s' and opacity %g on image%s.",
  1.2496 +                  x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",z0,sepz0=='%'?"%":"",
  1.2497 +                  color[0]?color:"default",opacity,gmic_inds);
  1.2498 +            cimg_foroff(indices,l) {
  1.2499 +              CImg<T> &img = images[indices[l]];
  1.2500 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2501 +              col.fill(color,true);
  1.2502 +              const int
  1.2503 +                nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  1.2504 +                ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
  1.2505 +                nz0 = (int)cimg::round(sepz0=='%'?z0*(img.dimz()-1)/100:z0,1);
  1.2506 +              gmic_apply(img,draw_point(nx0,ny0,nz0,col,opacity));
  1.2507 +            }
  1.2508 +          } else error("Draw point on image%s : Invalid argument '%s' "
  1.2509 +                       "(should be 'x[%%],y[%%][,z[%%][,opacity[,color]]])",gmic_inds,argument_text);
  1.2510 +          ++position; continue;
  1.2511 +        }
  1.2512 +
  1.2513 +        // Draw line.
  1.2514 +        if (!cimg::strcmp("-line",item0)) {
  1.2515 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, arg3[4096] = { 0 }, color[4096] = { 0 };
  1.2516 +          char sepx0 = 0, sepy0 = 0, sepx1 = 0, sepy1 = 0, end = 0;
  1.2517 +          float x0 = 0, y0 = 0, x1 = 0, y1 = 0, opacity = 1;
  1.2518 +          if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]"
  1.2519 +                          "%*c%f%*c%4095[0-9.eE,+-]",
  1.2520 +                          arg0,arg1,arg2,arg3,&opacity,color)>=4 &&
  1.2521 +              ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  1.2522 +               std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  1.2523 +              ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  1.2524 +               std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
  1.2525 +              ((std::sscanf(arg2,"%f%c%c",&x1,&sepx1,&end)==2 && sepx1=='%') ||
  1.2526 +               std::sscanf(arg2,"%f%c",&x1,&end)==1) &&
  1.2527 +              ((std::sscanf(arg3,"%f%c%c",&y1,&sepy1,&end)==2 && sepy1=='%') ||
  1.2528 +               std::sscanf(arg3,"%f%c",&y1,&end)==1)) {
  1.2529 +            print("Draw line (%g%s,%g%s) - (%g%s,%g%s) with color '%s' and opacity %g on image%s.",
  1.2530 +                  x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",x1,sepx1=='%'?"%":"",y1,sepy1=='%'?"%":"",
  1.2531 +                  color[0]?color:"default",opacity,gmic_inds);
  1.2532 +            cimg_foroff(indices,l) {
  1.2533 +              CImg<T> &img = images[indices[l]];
  1.2534 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2535 +              col.fill(color,true);
  1.2536 +              const int
  1.2537 +                nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  1.2538 +                ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
  1.2539 +                nx1 = (int)cimg::round(sepx1=='%'?x1*(img.dimx()-1)/100:x1,1),
  1.2540 +                ny1 = (int)cimg::round(sepy1=='%'?y1*(img.dimy()-1)/100:y1,1);
  1.2541 +              gmic_apply(img,draw_line(nx0,ny0,nx1,ny1,col,opacity));
  1.2542 +            }
  1.2543 +          } else error("Draw line on image%s : Invalid argument '%s' "
  1.2544 +                       "(should be 'x0[%%],y0[%%],x1[%%],y1[%%][,opacity[,color]]')",gmic_inds,argument_text);
  1.2545 +          ++position; continue;
  1.2546 +        }
  1.2547 +
  1.2548 +        // Draw polygon.
  1.2549 +        if (!cimg::strcmp("-polygon",item0)) {
  1.2550 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, tmp[4096] = { 0 }, sepx0 = 0, sepy0 = 0, end = 0;
  1.2551 +          int N = 0; float x0 = 0, y0 = 0, opacity = 1;
  1.2552 +          if (std::sscanf(argument,"%d%c",&N,&end)==2 && N>2) {
  1.2553 +            const char
  1.2554 +              *nargument = argument + std::sprintf(tmp,"%d",N) + 1,
  1.2555 +              *const eargument = argument + cimg::strlen(argument);
  1.2556 +            CImg<float> coords0(N,2,1,1,0);
  1.2557 +            CImg<bool> percents(N,2,1,1,0);
  1.2558 +            for (int n = 0; n<N; ++n) if (nargument<eargument) {
  1.2559 +              if (std::sscanf(nargument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]",arg0,arg1)==2 &&
  1.2560 +                  ((std::sscanf(arg0,"%f%c%c",&x0,&(sepx0=0),&end)==2 && sepx0=='%') ||
  1.2561 +                   std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  1.2562 +                  ((std::sscanf(arg1,"%f%c%c",&y0,&(sepy0=0),&end)==2 && sepy0=='%') ||
  1.2563 +                   std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
  1.2564 +                coords0(n,0) = x0; percents(n,0) = (sepx0=='%');
  1.2565 +                coords0(n,1) = y0; percents(n,1) = (sepy0=='%');
  1.2566 +                nargument+=cimg::strlen(arg0) + cimg::strlen(arg1) + 2;
  1.2567 +              } else error("Draw polygon on image%s : Invalid or incomplete argument '%s' "
  1.2568 +                           "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
  1.2569 +                           gmic_inds,argument_text);
  1.2570 +            } else error("Draw polygon on image%s : Incomplete argument '%s' "
  1.2571 +                         "(%d xy-coordinates should be defined)",
  1.2572 +                         gmic_inds,argument_text,N);
  1.2573 +            if (nargument<eargument && std::sscanf(nargument,"%4095[0-9.eE+-]",arg0)==1 &&
  1.2574 +                std::sscanf(arg0,"%f",&opacity)==1) nargument+=cimg::strlen(arg0)+1;
  1.2575 +            const char *const color = nargument<eargument?nargument:&(end=0);
  1.2576 +            print("Draw %d-vertices polygon with color '%s' and opacity %g on image%s.",
  1.2577 +                  N,color[0]?color:"default",opacity,gmic_inds);
  1.2578 +            cimg_foroff(indices,l) {
  1.2579 +              CImg<T> &img = images[indices[l]];
  1.2580 +              CImg<int> coords(coords0);
  1.2581 +              cimg_forX(coords,p) {
  1.2582 +                if (percents(p,0)) coords(p,0) = (int)cimg::round(coords0(p,0)*(img.dimx()-1)/100,1);
  1.2583 +                if (percents(p,1)) coords(p,1) = (int)cimg::round(coords0(p,1)*(img.dimy()-1)/100,1);
  1.2584 +              }
  1.2585 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2586 +              col.fill(color,true);
  1.2587 +              gmic_apply(img,draw_polygon(coords,col,opacity));
  1.2588 +            }
  1.2589 +          } else error("Draw polygon on image%s : Invalid argument '%s' "
  1.2590 +                       "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
  1.2591 +                       gmic_inds,argument_text);
  1.2592 +          ++position; continue;
  1.2593 +        }
  1.2594 +
  1.2595 +        // Draw ellipse.
  1.2596 +        if (!cimg::strcmp("-ellipse",item0)) {
  1.2597 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 };
  1.2598 +          char sepx0 = 0, sepy0 = 0, end = 0;
  1.2599 +          float x0 = 0, y0 = 0, r0 = 0, r1 = 0, ru = 1, rv = 0, opacity = 1;
  1.2600 +          if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%*c%f%*c%f%*c%f%*c%4095[0-9.eE,+-]",
  1.2601 +                          arg0,arg1,&r0,&r1,&ru,&rv,&opacity,color)>=4 &&
  1.2602 +              ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  1.2603 +               std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  1.2604 +              ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  1.2605 +               std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
  1.2606 +            print("Draw ellipse centered at (%g%s,%g%s) with radii (%g,%g), orientation (%g,%g), color '%s' "
  1.2607 +                  "and opacity %g on image%s.",
  1.2608 +                  x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",
  1.2609 +                  r0,r1,ru,rv,color[0]?color:"default",opacity,gmic_inds);
  1.2610 +            cimg_foroff(indices,l) {
  1.2611 +              CImg<T> &img = images[indices[l]];
  1.2612 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2613 +              col.fill(color,true);
  1.2614 +              const int
  1.2615 +                nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  1.2616 +                ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
  1.2617 +              gmic_apply(img,draw_ellipse(nx0,ny0,r0,r1,ru,rv,col,opacity));
  1.2618 +            }
  1.2619 +          } else error("Draw ellipse on image%s : Invalid argument '%s' "
  1.2620 +                       "(should be 'x[%%],y[%%],r,R[,u,v[,opacity[,color]]])",
  1.2621 +                       gmic_inds,argument_text);
  1.2622 +          ++position; continue;
  1.2623 +        }
  1.2624 +
  1.2625 +        // Draw text.
  1.2626 +        if (!cimg::strcmp("-text",item0)) {
  1.2627 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 }, text[4096] = { 0 };
  1.2628 +          char sepx0 = 0, sepy0 = 0, end = 0;
  1.2629 +          float x0 = 0, y0 = 0, opacity = 1; int siz = 11;
  1.2630 +          if (std::sscanf(argument,"%4095[^,],%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%*c%f%*c%4095[0-9.eE,+-]",
  1.2631 +                          text,arg0,arg1,&siz,&opacity,color)>=1 &&
  1.2632 +              ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  1.2633 +               std::sscanf(arg0,"%f%c",&x0,&end)==1 || !arg0[0]) &&
  1.2634 +              ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  1.2635 +               std::sscanf(arg1,"%f%c",&y0,&end)==1 || !arg1[0])) {
  1.2636 +            cimg::strclean(text); cimg::strescape(text);
  1.2637 +            print("Draw text \"%s\" at position (%g%s,%g%s) with font size %d, color '%s' "
  1.2638 +                  "and opacity %f on image%s.",
  1.2639 +                  text,x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",siz,color[0]?color:"default",opacity,gmic_inds);
  1.2640 +            cimg_foroff(indices,l) {
  1.2641 +              CImg<T> &img = images[indices[l]];
  1.2642 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2643 +              col.fill(color,true);
  1.2644 +              const int
  1.2645 +                nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  1.2646 +                ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
  1.2647 +              gmic_apply(img,draw_text(nx0,ny0,text,col.ptr(),0,opacity,siz));
  1.2648 +            }
  1.2649 +          } else error("Draw text on image%s : Invalid argument '%s' "
  1.2650 +                       "(should be 'text[,x[%%],y[%%][,size[,opacity[,color]]]]').",
  1.2651 +                       gmic_inds,argument_text);
  1.2652 +          ++position; continue;
  1.2653 +        }
  1.2654 +
  1.2655 +        // Draw image.
  1.2656 +        if (!cimg::strcmp("-image",item0)) {
  1.2657 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, sepz = 0, end = 0;
  1.2658 +          int ind0 = no_ind, indm0 = no_ind; float x = 0, y = 0, z = 0, opacity = 1;
  1.2659 +          if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==1 && sep==']') ||
  1.2660 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
  1.2661 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
  1.2662 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,arg2,&end)==4 ||
  1.2663 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,arg2,&opacity,&end)==5 ||
  1.2664 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c[%d%c%c",
  1.2665 +                            &ind0,arg0,arg1,arg2,&opacity,&indm0,&sep,&end)==7) &&
  1.2666 +              (!*arg0 ||
  1.2667 +               std::sscanf(arg0,"%f%c",&x,&end)==1 ||
  1.2668 +               (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
  1.2669 +              (!*arg1 ||
  1.2670 +               std::sscanf(arg1,"%f%c",&y,&end)==1 ||
  1.2671 +               (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%')) &&
  1.2672 +              (!*arg2 ||
  1.2673 +               std::sscanf(arg2,"%f%c",&z,&end)==1 ||
  1.2674 +               (std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%'))) {
  1.2675 +            gmic_check_indice(ind0,"Draw image on image%s");
  1.2676 +            const CImg<T> sprite = images[ind0];
  1.2677 +            CImg<T> mask;
  1.2678 +            if (indm0!=no_ind) {
  1.2679 +              gmic_check_indice(indm0,"Draw image on image%s");
  1.2680 +              mask = images[indm0];
  1.2681 +              print("Draw image [%d] at (%g%s,%g%s,%g%s), with mask [%d] and opacity %f on image%s.",
  1.2682 +                    ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",indm0,opacity,gmic_inds);
  1.2683 +            } else print("Draw image [%d] at (%g%s,%g%s,%g%s) with opacity %f on image%s.",
  1.2684 +                         ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",opacity,gmic_inds);
  1.2685 +            cimg_foroff(indices,l) {
  1.2686 +              CImg<T> &img = images[indices[l]];
  1.2687 +              const int
  1.2688 +                nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  1.2689 +                ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  1.2690 +                nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
  1.2691 +              if (indm0!=no_ind) { gmic_apply(img,draw_image(nx,ny,nz,sprite,mask,opacity)); }
  1.2692 +              else { gmic_apply(img,draw_image(nx,ny,nz,sprite,opacity)); }
  1.2693 +            }
  1.2694 +          } else error("Draw image on image%s : Invalid argument '%s' "
  1.2695 +                       "(should be '[indice][,x[%%][,y[%%][,z[%%][,opacity[,indice_mask]]]]]').",
  1.2696 +                       gmic_inds,argument_text);
  1.2697 +          ++position; continue;
  1.2698 +        }
  1.2699 +
  1.2700 +        // Draw 3D object.
  1.2701 +        if (!cimg::strcmp("-object3d",item0)) {
  1.2702 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, end = 0;
  1.2703 +          float x = 0, y = 0, z = 0, opacity = 1;
  1.2704 +          int ind0 = no_ind;
  1.2705 +          if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  1.2706 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
  1.2707 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
  1.2708 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,&z,&end)==4 ||
  1.2709 +               std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%c",&ind0,arg0,arg1,&z,&opacity,&end)==5) &&
  1.2710 +              (!*arg0 ||
  1.2711 +               std::sscanf(arg0,"%f%c",&x,&end)==1 ||
  1.2712 +               (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
  1.2713 +              (!*arg1 ||
  1.2714 +               std::sscanf(arg1,"%f%c",&y,&end)==1 ||
  1.2715 +               (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%'))) {
  1.2716 +            gmic_check_indice(ind0,"Draw 3D object on image%s");
  1.2717 +            if (!images[ind0].is_CImg3d())
  1.2718 +              error("Draw 3D object on image%s : Image [%d] is not a 3D object.",gmic_inds,ind0);
  1.2719 +            print("Draw 3D object [%d] at (%g%s,%g%s,%g) on image%s, with opacity %g.",
  1.2720 +                  ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,gmic_inds,opacity);
  1.2721 +            CImgList<unsigned int> primitives3d;
  1.2722 +            CImgList<unsigned char> colors3d;
  1.2723 +            CImg<float> opacities3d, points3d(images[ind0]);
  1.2724 +            points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  1.2725 +            opacities3d*=opacity;
  1.2726 +            cimg_foroff(indices,l) {
  1.2727 +              CImg<T> &img = images[indices[l]];
  1.2728 +              const float
  1.2729 +                nx = (float)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  1.2730 +                ny = (float)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1);
  1.2731 +              gmic_apply(img,draw_object3d(nx,ny,z,points3d,primitives3d,colors3d,opacities3d,
  1.2732 +                                           render3d,!is_oriented3d,focale3d,light3d_x,light3d_y,light3d_z,specular_light3d,
  1.2733 +                                           specular_shine3d,0));
  1.2734 +            }
  1.2735 +          } else error("Draw 3D object on image%s : Invalid argument '%s' "
  1.2736 +                       "(should be '[indice][,x[%%][,y[%%][,z[,opacity[,zoom[,u1,v1,w1,angle1[,...]]]]]]]').",
  1.2737 +                       gmic_inds,argument_text);
  1.2738 +          ++position; continue;
  1.2739 +        }
  1.2740 +
  1.2741 +        // Draw plasma fractal.
  1.2742 +        if (!cimg::strcmp("-plasma",item0)) {
  1.2743 +          float alpha = 1, beta = 1, opacity = 1; char end = 0;
  1.2744 +          if (std::sscanf(argument,"%f%c",&alpha,&end)==1 ||
  1.2745 +              std::sscanf(argument,"%f%*c%f%c",&alpha,&beta,&end)==2 ||
  1.2746 +              std::sscanf(argument,"%f%*c%f%*c%f%c",&alpha,&beta,&opacity,&end)==3) {
  1.2747 +            print("Draw plasma in image%s with alpha %g, beta %g and opacity %g.",gmic_inds,alpha,beta,opacity);
  1.2748 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],draw_plasma(alpha,beta,opacity));
  1.2749 +          } else error("Draw plasma in image%d : Invalid argument '%s' "
  1.2750 +                       "(should be 'alpha[,beta[,opacity]]').",gmic_inds,argument_text);
  1.2751 +          ++position; continue;
  1.2752 +        }
  1.2753 +
  1.2754 +        // Draw Mandelbrot/Julia fractal.
  1.2755 +        if (!cimg::strcmp("-mandelbrot",item0)) {
  1.2756 +          double z0r = -2, z0i = -2, z1r = 2, z1i = 2, paramr = 0, parami = 0; char end = 0;
  1.2757 +          float opacity = 1; int itermax = 100, julia = 0;
  1.2758 +          if (std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%c",&z0r,&z0i,&z1r,&z1i,&end)==4 ||
  1.2759 +              std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%c",&z0r,&z0i,&z1r,&z1i,&itermax,&end)==5 ||
  1.2760 +              std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%c",
  1.2761 +                          &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&end)==8 ||
  1.2762 +              std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%*c%f%c",
  1.2763 +                          &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&opacity,&end)==9) {
  1.2764 +            print("Draw %s fractal in image%s from complex area (%g,%g)-(%g,%g) with c0 = (%g,%g) (%d iterations).",
  1.2765 +                  julia?"Julia":"Mandelbrot",gmic_inds,z0r,z0i,z1r,z1i,paramr,parami,itermax);
  1.2766 +            cimg_foroff(indices,l)
  1.2767 +              gmic_apply(images[indices[l]],draw_mandelbrot(CImg<T>(),opacity,z0r,z0i,z1r,z1i,itermax,true,
  1.2768 +                                                            julia?true:false,paramr,parami));
  1.2769 +          } else error("Draw fractal in image%s : Invalid argument '%s' "
  1.2770 +                       "(should be 'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]').",gmic_inds,argument_text);
  1.2771 +          ++position; continue;
  1.2772 +        }
  1.2773 +
  1.2774 +        // Flood fill.
  1.2775 +        if (!cimg::strcmp("-flood",item0)) {
  1.2776 +          char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
  1.2777 +          char sepx = 0, sepy = 0, sepz = 0, end = 0;
  1.2778 +          float x = 0, y = 0, z = 0, tolerance = 0, opacity = 1;
  1.2779 +          if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%*c%4095[0-9.eE,+-]",
  1.2780 +                          arg0,arg1,arg2,&tolerance,&opacity,color)>=1 &&
  1.2781 +              ((std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
  1.2782 +               std::sscanf(arg0,"%f%c",&x,&end)==1) &&
  1.2783 +              ((std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
  1.2784 +               std::sscanf(arg1,"%f%c",&y,&end)==1 || !arg1[0]) &&
  1.2785 +              ((std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
  1.2786 +               std::sscanf(arg2,"%f%c",&z,&end)==1 || !arg2[0])) {
  1.2787 +            print("Flood fill image%s from (%g%s,%g%s,%g%s) with tolerance %g, opacity %g and color '%s'.",
  1.2788 +                  gmic_inds,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",tolerance,opacity,color);
  1.2789 +            cimg_foroff(indices,l) {
  1.2790 +              CImg<T> &img = images[indices[l]];
  1.2791 +              CImg<T> col(img.dimv(),1,1,1,0);
  1.2792 +              col.fill(color,true);
  1.2793 +              const int
  1.2794 +                nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  1.2795 +                ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  1.2796 +                nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
  1.2797 +              gmic_apply(img,draw_fill(nx,ny,nz,col,opacity,tolerance));
  1.2798 +            }
  1.2799 +          } else error("Flood fill image%s : Invalid argument '%s' "
  1.2800 +                       "(should be 'x[,y[,z[,tolerance[,opacity[,color]]]]]').",gmic_inds,argument_text);
  1.2801 +          ++position; continue;
  1.2802 +        }
  1.2803 +
  1.2804 +        //-------------------------
  1.2805 +        // Image list manipulation
  1.2806 +        //-------------------------
  1.2807 +
  1.2808 +        // Remove specified image(s).
  1.2809 +        if (!cimg::strcmp("-remove",item0) || !cimg::strcmp("-rm",item0)) {
  1.2810 +          print("Remove image%s",gmic_inds);
  1.2811 +          unsigned int off = 0;
  1.2812 +          cimg_foroff(indices,l) {
  1.2813 +            const unsigned int ind = indices[l] - off;
  1.2814 +            images.remove(ind); filenames.remove(ind);
  1.2815 +            ++off;
  1.2816 +          }
  1.2817 +          if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
  1.2818 +          continue;
  1.2819 +        }
  1.2820 +
  1.2821 +        // Keep specified image(s).
  1.2822 +        if (!cimg::strcmp("-keep",item0) || !cimg::strcmp("-k",item0)) {
  1.2823 +          print("Keep image%s",gmic_inds);
  1.2824 +          CImgList<T> nimages(indices.size());
  1.2825 +          cimg_foroff(indices,l) nimages[l].swap(images[indices[l]]);
  1.2826 +          nimages.transfer_to(images);
  1.2827 +          if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
  1.2828 +          continue;
  1.2829 +        }
  1.2830 +
  1.2831 +        // Move image(s) to specified position.
  1.2832 +        if (!cimg::strcmp("-move",item0) || !cimg::strcmp("-mv",item0)) {
  1.2833 +          int ind0 = no_ind; char end = 0;
  1.2834 +          if (std::sscanf(argument,"%d%c",&ind0,&end)==1) {
  1.2835 +            if (ind0<0) ind0+=images.size;
  1.2836 +            if (ind0<0) ind0 = 0;
  1.2837 +            if (ind0>(int)images.size) ind0 = images.size;
  1.2838 +            print("Move image%s to position %d.",gmic_inds,ind0);
  1.2839 +            CImgList<T> nimages;
  1.2840 +            CImgList<char> nfilenames;
  1.2841 +            cimg_foroff(indices,l) {
  1.2842 +              const unsigned int ind = indices[l];
  1.2843 +              nimages.insert(1); nimages.last().swap(images[ind]);
  1.2844 +              nfilenames.insert(1); nfilenames.last().swap(filenames[ind]);
  1.2845 +            }
  1.2846 +            images.insert(nimages,ind0); filenames.insert(nfilenames,ind0);
  1.2847 +            { cimglist_for(images,l) if (!images[l]) { images.remove(l); filenames.remove(l--); }}
  1.2848 +          } else error("Move image%s : Invalid argument '%s' "
  1.2849 +                       "(should be 'position').",gmic_inds,argument_text);
  1.2850 +          ++position; continue;
  1.2851 +        }
  1.2852 +
  1.2853 +        // Reverse images order.
  1.2854 +        if (!cimg::strcmp("-reverse",item0)) {
  1.2855 +          print("Reverse images order.");
  1.2856 +          CImgList<T> nimages(indices.size());
  1.2857 +          CImgList<char> nfilenames(indices.size());
  1.2858 +          cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }
  1.2859 +          nimages.reverse(); nfilenames.reverse();
  1.2860 +          { cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }}
  1.2861 +          continue;
  1.2862 +        }
  1.2863 +
  1.2864 +        // Set image name.
  1.2865 +        if (!cimg::strcmp("-name",item0)) {
  1.2866 +          cimg_foroff(indices,l) filenames[indices[l]].assign(argument,cimg::strlen(argument)+1,1,1,1,false);
  1.2867 +          ++position; continue;
  1.2868 +        }
  1.2869 +
  1.2870 +        //-------------------------
  1.2871 +        // 3D objects manipulation
  1.2872 +        //-------------------------
  1.2873 +
  1.2874 +        // Generate 3D cube.
  1.2875 +        if (!cimg::strcmp("-cube3d",item)) {
  1.2876 +          float size = 100; char end = 0;
  1.2877 +          if (std::sscanf(argument,"%f%c",&size,&end)==1) {
  1.2878 +            print("Generate 3D cube with size %g.",size);
  1.2879 +            CImgList<unsigned int> primitives3d;
  1.2880 +            CImg<float> points3d = CImg<T>::cube3d(primitives3d,size);
  1.2881 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2882 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2883 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2884 +            images.insert(points3d);
  1.2885 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2886 +          } else error("Generate 3D cube : Invalid argument '%s' "
  1.2887 +                       "(should be 'size').",argument_text);
  1.2888 +          ++position; continue;
  1.2889 +        }
  1.2890 +
  1.2891 +        // Generate 3D cone.
  1.2892 +        if (!cimg::strcmp("-cone3d",item)) {
  1.2893 +          float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
  1.2894 +          if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  1.2895 +              std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
  1.2896 +              std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
  1.2897 +            print("Generate 3D cone with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
  1.2898 +            CImgList<unsigned int> primitives3d;
  1.2899 +            CImg<float> points3d = CImg<T>::cone3d(primitives3d,radius,height,subdivisions);
  1.2900 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2901 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2902 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2903 +            images.insert(points3d);
  1.2904 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2905 +          } else error("Generate 3D cone : Invalid argument '%s' "
  1.2906 +                       "(should be 'radius[,height[,subdivisions]]').",argument_text);
  1.2907 +          ++position; continue;
  1.2908 +        }
  1.2909 +
  1.2910 +        // Generate 3D cylinder.
  1.2911 +        if (!cimg::strcmp("-cylinder3d",item)) {
  1.2912 +          float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
  1.2913 +          if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  1.2914 +              std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
  1.2915 +              std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
  1.2916 +            print("Generate 3D cylinder with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
  1.2917 +            CImgList<unsigned int> primitives3d;
  1.2918 +            CImg<float> points3d = CImg<T>::cylinder3d(primitives3d,radius,height,subdivisions);
  1.2919 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2920 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2921 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2922 +            images.insert(points3d);
  1.2923 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2924 +          } else error("Generate 3D cylinder : Invalid argument '%s' "
  1.2925 +                       "(should be 'radius[,height[,subdivisions]]').",argument_text);
  1.2926 +          ++position; continue;
  1.2927 +        }
  1.2928 +
  1.2929 +        // Generate 3D torus.
  1.2930 +        if (!cimg::strcmp("-torus3d",item)) {
  1.2931 +          float radius1 = 100, radius2 = 30; char end = 0; unsigned int subdivisions1 = 24, subdivisions2 = 12;
  1.2932 +          if (std::sscanf(argument,"%f%*c%f%c",&radius1,&radius2,&end)==2 ||
  1.2933 +              std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&radius1,&radius2,&subdivisions1,&subdivisions2,&end)==4) {
  1.2934 +            print("Generate 3D torus with radii %g and %g, and subdivisions %u and %u.",radius1,radius2,subdivisions1,subdivisions2);
  1.2935 +            CImgList<unsigned int> primitives3d;
  1.2936 +            CImg<float> points3d = CImg<T>::torus3d(primitives3d,radius1,radius2,subdivisions1,subdivisions2);
  1.2937 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2938 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2939 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2940 +            images.insert(points3d);
  1.2941 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2942 +          } else error("Generate 3D torus : Invalid argument '%s' "
  1.2943 +                       "(should be 'radius1,radius2[,subdivisions1,subdivisions2]').",argument_text);
  1.2944 +          ++position; continue;
  1.2945 +        }
  1.2946 +
  1.2947 +        // Generate 3D plane.
  1.2948 +        if (!cimg::strcmp("-plane3d",item)) {
  1.2949 +          float sizex = 100, sizey = 30; char end = 0; unsigned int subdivisionsx = 24, subdivisionsy = 12;
  1.2950 +          if (std::sscanf(argument,"%f%*c%f%c",&sizex,&sizey,&end)==2 ||
  1.2951 +              std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&sizex,&sizey,&subdivisionsx,&subdivisionsy,&end)==4) {
  1.2952 +            print("Generate 3D plane with dimensions %g and %g, and subdivisions %u and %u.",sizex,sizey,subdivisionsx,subdivisionsy);
  1.2953 +            CImgList<unsigned int> primitives3d;
  1.2954 +            CImg<float> points3d = CImg<T>::plane3d(primitives3d,sizex,sizey,subdivisionsx,subdivisionsy);
  1.2955 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2956 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2957 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2958 +            images.insert(points3d);
  1.2959 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2960 +          } else error("Generate 3D plane : Invalid argument '%s' "
  1.2961 +                       "(should be 'sizex,sizey[,subdivisionsx,subdivisionsy]').",argument_text);
  1.2962 +          ++position; continue;
  1.2963 +        }
  1.2964 +
  1.2965 +        // Generate 3D sphere.
  1.2966 +        if (!cimg::strcmp("-sphere3d",item)) {
  1.2967 +          float radius = 100; char end = 0; unsigned int subdivisions = 3;
  1.2968 +          if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  1.2969 +              std::sscanf(argument,"%f%*c%u%c",&radius,&subdivisions,&end)==2) {
  1.2970 +            print("Generate 3D sphere with radius %g and %u subdivisions.",radius,subdivisions);
  1.2971 +            CImgList<unsigned int> primitives3d;
  1.2972 +            CImg<float> points3d = CImg<T>::sphere3d(primitives3d,radius,subdivisions);
  1.2973 +            CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  1.2974 +            CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  1.2975 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.2976 +            images.insert(points3d);
  1.2977 +            filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  1.2978 +          } else error("Generate 3D sphere : Invalid argument '%s' "
  1.2979 +                       "(should be 'radius[,subdivisions]').",argument_text);
  1.2980 +          ++position; continue;
  1.2981 +        }
  1.2982 +
  1.2983 +        // Build 3D elevation.
  1.2984 +        if (!cimg::strcmp("-elevation3d",item0)) {
  1.2985 +          float zfact = 0.2f; char end = 0, sep = 0; int ind0 = no_ind;
  1.2986 +          if (std::sscanf(argument,"%f%c",&zfact,&end)==1 ||
  1.2987 +              (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')) {
  1.2988 +            CImg<typename CImg<T>::Tfloat> elev;
  1.2989 +            if (ind0!=no_ind) {
  1.2990 +              gmic_check_indice(ind0,"Build 3D elevation of image%s");
  1.2991 +              print("Build 3D elevation of image%s with elevation map [%d].",gmic_inds,ind0);
  1.2992 +              if (images[ind0].dimv()==1) elev = images[ind0];
  1.2993 +              else elev = images[ind0].get_pointwise_norm();
  1.2994 +            } else print("Build 3D elevation of image%s with z-factor %g.",gmic_inds,zfact);
  1.2995 +            cimg_foroff(indices,l) {
  1.2996 +              CImg<T>& img = images[indices[l]];
  1.2997 +              CImgList<unsigned int> primitives3d;
  1.2998 +              CImgList<unsigned char> colors3d;
  1.2999 +              CImg<float> opacities3d, points3d;
  1.3000 +              if (elev) points3d = img.get_elevation3d(primitives3d,colors3d,elev);
  1.3001 +              else {
  1.3002 +                if (img.dimv()==1) (elev = img)*=zfact; else (elev = img.get_pointwise_norm())*=zfact;
  1.3003 +                points3d = img.get_elevation3d(primitives3d,colors3d,elev);
  1.3004 +                elev.assign();
  1.3005 +              }
  1.3006 +              opacities3d.assign(1,primitives3d.size,1,1,1);
  1.3007 +              points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3008 +              gmic_apply(img,replace(points3d));
  1.3009 +            }
  1.3010 +          } else error("Build 3D elevation : invalid argument '%s' "
  1.3011 +                       "(should be 'z-factor' or '[indice]').",argument_text);
  1.3012 +          ++position; continue;
  1.3013 +        }
  1.3014 +
  1.3015 +        // Build 3D isovalue.
  1.3016 +        if (!cimg::strcmp("-isovalue3d",item0)) {
  1.3017 +          float value = 0; char end = 0;
  1.3018 +          if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  1.3019 +            print("Build 3D isovalue %g of image%s.",value,gmic_inds);
  1.3020 +            cimg_foroff(indices,l) {
  1.3021 +              const unsigned int ind = indices[l];
  1.3022 +              CImg<T>& img = images[ind];
  1.3023 +              CImg<float> points3d;
  1.3024 +              CImgList<unsigned int> primitives3d;
  1.3025 +              CImgList<unsigned char> colors3d;
  1.3026 +              CImg<float> opacities3d;
  1.3027 +              CImg<unsigned char> palette;
  1.3028 +              palette.assign(3,img.dim,1,1,220).noise(35,1);
  1.3029 +              if (img.dim==1) palette(0) = palette(1) = palette(2) = 255;
  1.3030 +              else {
  1.3031 +                palette(0,0) = 255; palette(1,0) = 30; palette(2,0) = 30;
  1.3032 +                palette(0,1) = 30; palette(1,1) = 255; palette(2,1) = 30;
  1.3033 +                if (img.dim>=3) palette(0,2) = 30; palette(1,2) = 30; palette(2,2) = 255;
  1.3034 +              }
  1.3035 +              cimg_forV(img,k) {
  1.3036 +                CImgList<unsigned int> prims;
  1.3037 +                const CImg<float> pts = img.get_shared_channel(k).get_isovalue3d(prims,value);
  1.3038 +                if (pts) {
  1.3039 +                  points3d.append_object3d(primitives3d,pts,prims);
  1.3040 +                  colors3d.insert(prims.size,
  1.3041 +                                  CImg<unsigned char>::vector(palette(0,k),palette(1,k),palette(2,k)));
  1.3042 +                }
  1.3043 +              }
  1.3044 +              opacities3d.assign(1,primitives3d.size,1,1,1);
  1.3045 +              if (!points3d)
  1.3046 +                warning("Build 3D isovalue of image [%u] : Isovalue %g not found.",ind,value);
  1.3047 +              else points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3048 +              gmic_apply(img,replace(points3d));
  1.3049 +            }
  1.3050 +          } else error("Build 3D isovalue of image%s : Invalid argument '%s' "
  1.3051 +                       "(should be 'isovalue').",gmic_inds,argument_text);
  1.3052 +          ++position; continue;
  1.3053 +        }
  1.3054 +
  1.3055 +        // Center a 3D object.
  1.3056 +        if (!cimg::strcmp("-center3d",item0) || !cimg::strcmp("-c3d",item0)) {
  1.3057 +          print("Center 3D object%s.",gmic_inds);
  1.3058 +          cimg_foroff(indices,l) {
  1.3059 +            const unsigned int ind = indices[l];
  1.3060 +            if (!images[ind].is_CImg3d())
  1.3061 +              error("Center 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3062 +            gmic_apply(images[ind],centerCImg3d());
  1.3063 +          }
  1.3064 +          continue;
  1.3065 +        }
  1.3066 +
  1.3067 +        // Normalize a 3D object.
  1.3068 +        if (!cimg::strcmp("-normalize3d",item0) || !cimg::strcmp("-n3d",item0)) {
  1.3069 +          print("Normalize 3D object%s.",gmic_inds);
  1.3070 +          cimg_foroff(indices,l) {
  1.3071 +            const unsigned int ind = indices[l];
  1.3072 +            if (!images[ind].is_CImg3d())
  1.3073 +              error("Normalize 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3074 +            gmic_apply(images[ind],normalizeCImg3d());
  1.3075 +          }
  1.3076 +          continue;
  1.3077 +        }
  1.3078 +
  1.3079 +        // Rotate a 3D object.
  1.3080 +        if (!cimg::strcmp("-rotate3d",item0) || !cimg::strcmp("-rot3d",item0)) {
  1.3081 +          float u = 0, v = 0, w = 1, angle = 0; char end = 0;
  1.3082 +          if (std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&u,&v,&w,&angle,&end)==4) {
  1.3083 +            print("Rotate 3D object%s around axis (%g,%g,%g) with angle %g.",gmic_inds,u,v,w,angle);
  1.3084 +            const CImg<float> rot = CImg<float>::rotation_matrix(u,v,w,(float)(angle*cimg::valuePI/180));
  1.3085 +            cimg_foroff(indices,l) {
  1.3086 +              const unsigned int ind = indices[l];
  1.3087 +              if (!images[ind].is_CImg3d())
  1.3088 +                error("Rotate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3089 +              gmic_apply(images[ind],rotateCImg3d(rot));
  1.3090 +            }
  1.3091 +          } else error("Rotate 3D object%s : Invalid argument '%s' "
  1.3092 +                       "(should be 'u,v,w,angle').",gmic_inds,argument_text);
  1.3093 +          ++position; continue;
  1.3094 +        }
  1.3095 +
  1.3096 +        // Add 3D objects together or translate a 3D object.
  1.3097 +        if (!cimg::strcmp("-add3d",item0) || !cimg::strcmp("-+3d",item0)) {
  1.3098 +          float tx = 0, ty = 0, tz = 0; int ind0 = no_ind; char sep = 0, end = 0;
  1.3099 +          if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
  1.3100 +              std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
  1.3101 +              std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
  1.3102 +            print("Translate 3D object%s with vector (%g,%g,%g).",gmic_inds,tx,ty,tz);
  1.3103 +            cimg_foroff(indices,l) {
  1.3104 +              const unsigned int ind = indices[l];
  1.3105 +              if (!images[ind].is_CImg3d())
  1.3106 +                error("Translate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3107 +              gmic_apply(images[ind],translateCImg3d(tx,ty,tz));
  1.3108 +            }
  1.3109 +            ++position;
  1.3110 +          } else if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  1.3111 +            gmic_check_indice(ind0,"Merge object with 3D object%s.");
  1.3112 +            const CImg<T> img0 = images[ind0];
  1.3113 +            if (!img0.is_CImg3d()) error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind0);
  1.3114 +            print("Merge object [%d] with 3D object%s.",ind0,gmic_inds);
  1.3115 +            cimg_foroff(indices,l) {
  1.3116 +              const unsigned int ind = indices[l];
  1.3117 +              const CImg<T> &img = images[ind];
  1.3118 +              if (!img.is_CImg3d())
  1.3119 +                error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind);
  1.3120 +              gmic_apply(images[ind],appendCImg3d(img0));
  1.3121 +            }
  1.3122 +            ++position;
  1.3123 +          } else {
  1.3124 +            print("Merge 3D object%s together.",gmic_inds);
  1.3125 +            if (indices) {
  1.3126 +              const unsigned int ind0 = indices[0];
  1.3127 +              if (!images[ind0].is_CImg3d())
  1.3128 +                error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind0);
  1.3129 +              for (unsigned int siz = indices.size(), off = 0, l = 1; l<siz; ++l) {
  1.3130 +                const unsigned int ind = indices[l] - off;
  1.3131 +                if (!images[ind].is_CImg3d())
  1.3132 +                  error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3133 +                images[ind0].appendCImg3d(images[ind]);
  1.3134 +                images.remove(ind); filenames.remove(ind);
  1.3135 +                ++off;
  1.3136 +              }
  1.3137 +            }
  1.3138 +          }
  1.3139 +          continue;
  1.3140 +        }
  1.3141 +
  1.3142 +        // Translate 3D object by the opposite vector.
  1.3143 +        if (!cimg::strcmp("-sub3d",item0) || !cimg::strcmp("--3d",item0)) {
  1.3144 +          float tx = 0, ty = 0, tz = 0; char end = 0;
  1.3145 +          if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
  1.3146 +              std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
  1.3147 +              std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
  1.3148 +            print("Translate 3D object%s with vector -(%g,%g,%g).",gmic_inds,tx,ty,tz);
  1.3149 +            cimg_foroff(indices,l) {
  1.3150 +              CImg<T>& img = images[indices[l]];
  1.3151 +              CImgList<unsigned int> primitives3d;
  1.3152 +              CImgList<unsigned char> colors3d;
  1.3153 +              CImg<float> opacities3d;
  1.3154 +              CImg<T> points3d;
  1.3155 +              if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  1.3156 +              points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  1.3157 +              points3d.get_shared_line(0)-=tx;
  1.3158 +              points3d.get_shared_line(1)-=ty;
  1.3159 +              points3d.get_shared_line(2)-=tz;
  1.3160 +              points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3161 +              if (get_version) {
  1.3162 +                images.insert(1); points3d.transfer_to(images.last());
  1.3163 +                filenames.insert(filenames[indices[l]]);
  1.3164 +              } else points3d.transfer_to(images[indices[l]]);
  1.3165 +            }
  1.3166 +          } else error("Translate 3D object%s : Invalid argument '%s' "
  1.3167 +                       "(should be 'tx,ty,tz').",gmic_inds,argument_text);
  1.3168 +          ++position; continue;
  1.3169 +        }
  1.3170 +
  1.3171 +        // Scale a 3D object.
  1.3172 +        bool divide = false;
  1.3173 +        if (!cimg::strcmp("-mul3d",item0) || !cimg::strcmp("-*3d",item0) ||
  1.3174 +            ((divide=true)==true && (!cimg::strcmp("-div3d",item0) || !cimg::strcmp("-/3d",item0)))) {
  1.3175 +          float sx = 0, sy = 1, sz = 1; char end = 0;
  1.3176 +          if ((std::sscanf(argument,"%f%c",&sx,&end)==1 && (sy = sz = sx),1) ||
  1.3177 +              std::sscanf(argument,"%f%*c%f%c",&sx,&sy,&end)==2 ||
  1.3178 +              std::sscanf(argument,"%f%*c%f%*c%f%c",&sx,&sy,&sz,&end)==3) {
  1.3179 +            if (divide) print("Scale 3D object%s with factors (1/%g,1/%g,1/%g).",gmic_inds,sx,sy,sz);
  1.3180 +            else print("Scale 3D object%s with factors (%g,%g,%g).",gmic_inds,sx,sy,sz);
  1.3181 +            cimg_foroff(indices,l) {
  1.3182 +              CImg<T>& img = images[indices[l]];
  1.3183 +              CImgList<unsigned int> primitives3d;
  1.3184 +              CImgList<unsigned char> colors3d;
  1.3185 +              CImg<float> opacities3d;
  1.3186 +              CImg<T> points3d;
  1.3187 +              if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  1.3188 +              points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  1.3189 +              if (divide) {
  1.3190 +                points3d.get_shared_line(0)/=sx;
  1.3191 +                points3d.get_shared_line(1)/=sy;
  1.3192 +                points3d.get_shared_line(2)/=sz;
  1.3193 +              } else {
  1.3194 +                points3d.get_shared_line(0)*=sx;
  1.3195 +                points3d.get_shared_line(1)*=sy;
  1.3196 +                points3d.get_shared_line(2)*=sz;
  1.3197 +              }
  1.3198 +              points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3199 +              if (get_version) {
  1.3200 +                images.insert(1); points3d.transfer_to(images.last());
  1.3201 +                filenames.insert(filenames[indices[l]]);
  1.3202 +              } else points3d.transfer_to(images[indices[l]]);
  1.3203 +            }
  1.3204 +          } else error("Scale 3D object%s : Invalid argument '%s' "
  1.3205 +                       "(should be 'fact' or 'factx,facty[,factz]').",gmic_inds,argument_text);
  1.3206 +          ++position; continue;
  1.3207 +        }
  1.3208 +
  1.3209 +        // Set color of 3D object(s).
  1.3210 +        if (!cimg::strcmp("-color3d",item0) || !cimg::strcmp("-col3d",item0)) {
  1.3211 +          float R = 200, G = 200, B = 200, opacity = -1; char end = 0;
  1.3212 +          if (std::sscanf(argument,"%f%*c%f%*c%f%c",&R,&G,&B,&end)==3 ||
  1.3213 +              std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&R,&G,&B,&opacity,&end)==4) {
  1.3214 +            const bool set_opacity = (opacity>=0);
  1.3215 +            R = (float)cimg::round(R,1); G = (float)cimg::round(G,1); B = (float)cimg::round(B,1);
  1.3216 +            if (R<0) R = 0; if (R>255) R = 255;
  1.3217 +            if (G<0) G = 0; if (G>255) G = 255;
  1.3218 +            if (B<0) B = 0; if (B>255) B = 255;
  1.3219 +            if (set_opacity) print("Set colors of 3D object%s to (%g,%g,%g) and opacity to %g.",gmic_inds,R,G,B,opacity);
  1.3220 +            else print("Set color of 3D object%s to (%g,%g,%g).",gmic_inds,R,G,B);
  1.3221 +            cimg_foroff(indices,l) {
  1.3222 +              const unsigned int ind = indices[l];
  1.3223 +              if (!images[ind].is_CImg3d())
  1.3224 +                error("Set color of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3225 +              gmic_apply(images[ind],coloropacityCImg3d(R,G,B,opacity,true,set_opacity));
  1.3226 +            }
  1.3227 +          } else error("Set color of 3D object%s : Invalid argument '%s' "
  1.3228 +                       "(should be 'R,G,B[,opacity]').",gmic_inds,argument_text);
  1.3229 +          ++position; continue;
  1.3230 +        }
  1.3231 +
  1.3232 +        // Set opacity of 3D object(s).
  1.3233 +        if (!cimg::strcmp("-opacity3d",item0) || !cimg::strcmp("-opac3d",item0)) {
  1.3234 +          float opacity = 1; char end = 0;
  1.3235 +          if (std::sscanf(argument,"%f%c",&opacity,&end)==1) {
  1.3236 +            print("Set opacity of 3D object%s to %g.",gmic_inds,opacity);
  1.3237 +            cimg_foroff(indices,l) {
  1.3238 +              const unsigned int ind = indices[l];
  1.3239 +              if (!images[ind].is_CImg3d())
  1.3240 +                error("Set opacity of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  1.3241 +              gmic_apply(images[ind],coloropacityCImg3d(0,0,0,opacity,false,true));
  1.3242 +            }
  1.3243 +          } else error("Set opacity of 3D object%s : Invalid argument '%s' "
  1.3244 +                       "(should be 'opacity').",gmic_inds,argument_text);
  1.3245 +          ++position; continue;
  1.3246 +        }
  1.3247 +
  1.3248 +        // Invert 3D orientation.
  1.3249 +        if (!cimg::strcmp("-invert3d",item0) || !cimg::strcmp("-i3d",item0)) {
  1.3250 +          print("Invert orientation of 3D object%s.",gmic_inds);
  1.3251 +          cimg_foroff(indices,l) {
  1.3252 +            CImg<T> &img = images[indices[l]];
  1.3253 +            CImgList<unsigned int> primitives3d;
  1.3254 +            CImgList<unsigned char> colors3d;
  1.3255 +            CImg<float> opacities3d;
  1.3256 +            CImg<T> points3d;
  1.3257 +            if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  1.3258 +            points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  1.3259 +            if (primitives3d) primitives3d.invert_object3d();
  1.3260 +            points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3261 +            if (get_version) {
  1.3262 +              images.insert(1); points3d.transfer_to(images.last());
  1.3263 +              filenames.insert(filenames[indices[l]]);
  1.3264 +            } else points3d.transfer_to(images[indices[l]]);
  1.3265 +          }
  1.3266 +          continue;
  1.3267 +        }
  1.3268 +
  1.3269 +        // Split 3D object(s) into 6 vector images {header,N,vertices,primitives,colors,opacities}
  1.3270 +        if (!cimg::strcmp("-split3d",item0) || !cimg::strcmp("-s3d",item0)) {
  1.3271 +          print("Split 3D object%s into its different characteristics.",gmic_inds);
  1.3272 +          unsigned int off = 0;
  1.3273 +          cimg_foroff(indices,l) {
  1.3274 +            const unsigned int ind = indices[l] + off;
  1.3275 +            CImg<T> &img = images[ind];
  1.3276 +            const CImg<char> filename = filenames[ind];
  1.3277 +            CImgList<unsigned int> primitives3d;
  1.3278 +            CImgList<unsigned char> colors3d;
  1.3279 +            CImg<float> opacities3d;
  1.3280 +            CImg<T> points3d;
  1.3281 +            if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  1.3282 +            points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  1.3283 +            CImgList<T> split;
  1.3284 +            split.insert(CImg<T>("CImg3d",1,6,1,1,false)+=0.5f);
  1.3285 +            split.insert(CImg<T>::vector((T)points3d.dimx(),(T)primitives3d.size));
  1.3286 +            split.insert(1); points3d.resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(split.last());
  1.3287 +            points3d.assign();
  1.3288 +            CImgList<T> _prims;
  1.3289 +            cimglist_for(primitives3d,p)
  1.3290 +              _prims.insert(CImg<T>::vector((T)primitives3d[p].size())).insert(primitives3d[p]).last().unroll('y');
  1.3291 +            primitives3d.assign();
  1.3292 +            split.insert(_prims.get_append('y')); _prims.assign();
  1.3293 +            split.insert(colors3d.get_append('x').transpose().unroll('y')); colors3d.assign();
  1.3294 +            split.insert(1); opacities3d.transfer_to(split.last());
  1.3295 +            if (get_version) {
  1.3296 +              images.insert(split);
  1.3297 +              filenames.insert(split.size,filename);
  1.3298 +            } else {
  1.3299 +              images.remove(ind); images.insert(split,ind);
  1.3300 +              filenames.remove(ind); filenames.insert(split.size,filename,ind);
  1.3301 +              off+=split.size-1;
  1.3302 +            }
  1.3303 +          }
  1.3304 +          continue;
  1.3305 +        }
  1.3306 +
  1.3307 +        // Set 3D light position.
  1.3308 +        if (!cimg::strcmp("-light3d",item) || !cimg::strcmp("-l3d",item)) {
  1.3309 +          float lx = 0, ly = 0, lz = -5000; char end = 0;
  1.3310 +          if (std::sscanf(argument,"%f%*c%f%*c%f%c",&lx,&ly,&lz,&end)==3) {
  1.3311 +            print("Set 3D light position at (%g,%g,%g).",lx,ly,lz);
  1.3312 +            light3d_x = lx;
  1.3313 +            light3d_y = ly;
  1.3314 +            light3d_z = lz;
  1.3315 +          } else error("Set 3D light position : Invalid argument '%s' "
  1.3316 +                       "(should be 'posx,posy,posz').",argument_text);
  1.3317 +          ++position; continue;
  1.3318 +        }
  1.3319 +
  1.3320 +        // Set 3D focale.
  1.3321 +        if (!cimg::strcmp("-focale3d",item) || !cimg::strcmp("-f3d",item)) {
  1.3322 +          float focale = 500; char end = 0;
  1.3323 +          if (std::sscanf(argument,"%f%c",&focale,&end)==1) {
  1.3324 +            focale3d = focale;
  1.3325 +            print("Set 3D focale to %g.",focale);
  1.3326 +          } else error("Set 3D focale : Invalid argument '%s' "
  1.3327 +                       "(should be 'value').");
  1.3328 +          ++position; continue;
  1.3329 +        }
  1.3330 +
  1.3331 +        // Set 3D specular light parameters.
  1.3332 +        if (!cimg::strcmp("-specl3d",item) || !cimg::strcmp("-sl3d",item)) {
  1.3333 +          float value = 0; char end = 0;
  1.3334 +          if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  1.3335 +            specular_light3d = value;
  1.3336 +            print("Set amount of 3D specular light to %g.",specular_light3d);
  1.3337 +          }
  1.3338 +          else error("Set amount of 3D specular light : invalid argument '%s'"
  1.3339 +                     "(should be 'value').",
  1.3340 +                     argument_text);
  1.3341 +          ++position; continue;
  1.3342 +        }
  1.3343 +
  1.3344 +        if (!cimg::strcmp("-specs3d",item) || !cimg::strcmp("-ss3d",item)) {
  1.3345 +          float value = 0; char end = 0;
  1.3346 +          if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  1.3347 +            specular_shine3d = value;
  1.3348 +            print("Set shininess of 3D specular light to %g.",specular_shine3d);
  1.3349 +          }
  1.3350 +          else error("Set shininess of 3D specular light : invalid argument '%s'"
  1.3351 +                     "(should be 'value').",
  1.3352 +                     argument_text);
  1.3353 +          ++position; continue;
  1.3354 +        }
  1.3355 +
  1.3356 +        // Switch double-sided mode for 3D rendering.
  1.3357 +        if (!cimg::strcmp("-orient3d",item) || !cimg::strcmp("-o3d",item)) {
  1.3358 +          is_oriented3d = !is_oriented3d;
  1.3359 +          continue;
  1.3360 +        }
  1.3361 +
  1.3362 +        // Set 3D rendering mode.
  1.3363 +        if (!cimg::strcmp("-render3d",item) || !cimg::strcmp("-r3d",item)) {
  1.3364 +          unsigned int value = 0; char end = 0;
  1.3365 +          if (std::sscanf(argument,"%u%c",&value,&end)==1) {
  1.3366 +            render3d = value;
  1.3367 +            print("Set static 3D render mode to %s.",
  1.3368 +                  render3d==-1?"bounding-box":
  1.3369 +                  render3d==0?"pointwise":render3d==1?"linear":render3d==2?"flat":
  1.3370 +                  render3d==3?"flat-shaded":render3d==4?"Gouraud-shaded":
  1.3371 +                  render3d==5?"Phong-shaded":"none");
  1.3372 +          }
  1.3373 +          else error("Set static 3D render mode : invalid argument '%s'"
  1.3374 +                     "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
  1.3375 +                     argument_text);
  1.3376 +          ++position; continue;
  1.3377 +        }
  1.3378 +
  1.3379 +        if (!cimg::strcmp("-renderd3d",item) || !cimg::strcmp("-rd3d",item)) {
  1.3380 +          unsigned int value = 0; char end = 0;
  1.3381 +          if (std::sscanf(argument,"%u%c",&value,&end)==1) {
  1.3382 +            renderd3d = value;
  1.3383 +            print("Set dynamic 3D render mode to %s.",
  1.3384 +                  renderd3d==-1?"bounding-box":
  1.3385 +                  renderd3d==0?"pointwise":renderd3d==1?"linear":renderd3d==2?"flat":
  1.3386 +                  renderd3d==3?"flat-shaded":renderd3d==4?"Gouraud-shaded":
  1.3387 +                  renderd3d==5?"Phong-shaded":"none");
  1.3388 +          }
  1.3389 +          else error("Set dynamic 3D render mode : invalid argument '%s'"
  1.3390 +                     "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
  1.3391 +                     argument_text);
  1.3392 +          ++position; continue;
  1.3393 +        }
  1.3394 +
  1.3395 +        // Set 3D background color.
  1.3396 +        if (!cimg::strcmp("-background3d",item) || !cimg::strcmp("-b3d",item)) {
  1.3397 +          int R = 0, G = 0, B = 0; char end = 0;
  1.3398 +          const int nb = std::sscanf(argument,"%d%*c%d%*c%d%c",&R,&G,&B,&end);
  1.3399 +          switch (nb) {
  1.3400 +          case 1 : background3d[0] = background3d[1] = background3d[2] = R; break;
  1.3401 +          case 2 : background3d[0] = R; background3d[1] = background3d[2] = G; break;
  1.3402 +          case 3 : background3d[0] = R; background3d[1] = G; background3d[2] = B; break;
  1.3403 +          default: error("Set 3D background color : Invalid argument '%s'.",argument_text);
  1.3404 +          }
  1.3405 +          print("Set 3D background color to (%d,%d,%d).",
  1.3406 +                (int)background3d[0],(int)background3d[1],(int)background3d[2]);
  1.3407 +          ++position; continue;
  1.3408 +        }
  1.3409 +
  1.3410 +        //----------------
  1.3411 +        // Other commands.
  1.3412 +        //----------------
  1.3413 +
  1.3414 +        // No operations : do nothing
  1.3415 +        if (!cimg::strcmp("-nop",item)) {
  1.3416 +          continue;
  1.3417 +        }
  1.3418 +
  1.3419 +        // Skip next argument;
  1.3420 +        if (!cimg::strcmp("-skip",item)) {
  1.3421 +          ++position;
  1.3422 +          continue;
  1.3423 +        }
  1.3424 +
  1.3425 +        // Echo.
  1.3426 +        if (!cimg::strcmp("-echo",item) || !cimg::strcmp("-e",item)) {
  1.3427 +          const int l = cimg::strlen(argument);
  1.3428 +          if (l>=2 && argument[0]=='"' && argument[l-1]=='"') {
  1.3429 +            if (l==2) print(""); else {
  1.3430 +              CImg<char> nargument(argument+1,l-1,1,1,1,false);
  1.3431 +              nargument(l-2)=0;
  1.3432 +              print("%s",nargument.ptr());
  1.3433 +            }
  1.3434 +          } else print("%s",argument);
  1.3435 +          ++position; continue;
  1.3436 +        }
  1.3437 +
  1.3438 +        // Print.
  1.3439 +        if (!cimg::strcmp("-print",item0) || !cimg::strcmp("-p",item0)) {
  1.3440 +          if (images.size) {
  1.3441 +            print("Print image%s.\n\n",gmic_inds);
  1.3442 +            char title[4096];
  1.3443 +            if (verbosity_level>=0) cimg_foroff(indices,l) {
  1.3444 +              const unsigned int ind = indices[l];
  1.3445 +              std::sprintf(title,"image [%u] = '%s'",ind,filenames[ind].ptr());
  1.3446 +              images[ind].print(title);
  1.3447 +            }
  1.3448 +            is_released = true;
  1.3449 +          } else print("Print image[].");
  1.3450 +          continue;
  1.3451 +        }
  1.3452 +
  1.3453 +        // Quit.
  1.3454 +        if (!cimg::strcmp("-quit",item) || !cimg::strcmp("-q",item)) {
  1.3455 +          print("Quit.");
  1.3456 +          is_released = true;
  1.3457 +          dowhile.assign();
  1.3458 +          repeatdone.assign();
  1.3459 +          position = command_line.size;
  1.3460 +          continue;
  1.3461 +        }
  1.3462 +
  1.3463 +        // Do...while.
  1.3464 +        if (!cimg::strcmp("-do",item)) {
  1.3465 +          dowhile.insert(CImg<int>::vector((int)position));
  1.3466 +          continue;
  1.3467 +        }
  1.3468 +
  1.3469 +        if (!cimg::strcmp("-while",item)) {
  1.3470 +          double cond = 0; char end = 0;
  1.3471 +          if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
  1.3472 +          if (!dowhile) error("Directive '-while' is not associated with a '-do' command.");
  1.3473 +          if (cond<=0) dowhile.remove();
  1.3474 +          else { position = (unsigned int)dowhile.last()(0); continue; }
  1.3475 +          ++position; continue;
  1.3476 +        }
  1.3477 +
  1.3478 +        // If..else..endif
  1.3479 +        if (!cimg::strcmp("-if",item)) {
  1.3480 +          double cond = 0; char end = 0;
  1.3481 +          if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
  1.3482 +          if (cond<=0) {
  1.3483 +            for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
  1.3484 +              const char *it = command_line[position].ptr();
  1.3485 +              if (!cimg::strcmp("-if",it)) ++nbifs;
  1.3486 +              if (!cimg::strcmp("-endif",it)) --nbifs;
  1.3487 +              if (!cimg::strcmp("-else",it) && nbifs==1) --nbifs;
  1.3488 +            }
  1.3489 +            continue;
  1.3490 +          }
  1.3491 +          ++position; continue;
  1.3492 +        }
  1.3493 +        if (!cimg::strcmp("-else",item)) {
  1.3494 +          for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
  1.3495 +            if (!cimg::strcmp("-if",command_line[position].ptr())) ++nbifs;
  1.3496 +            if (!cimg::strcmp("-endif",command_line[position].ptr())) --nbifs;
  1.3497 +          }
  1.3498 +          continue;
  1.3499 +        }
  1.3500 +        if (!cimg::strcmp("-endif",item)) continue;
  1.3501 +
  1.3502 +        // Repeat...done
  1.3503 +        if (!cimg::strcmp("-repeat",item)) {
  1.3504 +          float fnb = 0; char end = 0;
  1.3505 +          if (std::sscanf(argument,"%f%c",&fnb,&end)==1) {
  1.3506 +            const int nb = (int)fnb;
  1.3507 +            if (nb>0) repeatdone.insert(CImg<int>::vector((int)position+1,nb));
  1.3508 +            else {
  1.3509 +              int nbrepeats = 0;
  1.3510 +              for (nbrepeats = 1; nbrepeats && position<command_line.size; ++position) {
  1.3511 +                const char *it = command_line[position].ptr();
  1.3512 +                if (!cimg::strcmp("-repeat",it)) ++nbrepeats;
  1.3513 +                if (!cimg::strcmp("-done",it)) --nbrepeats;
  1.3514 +              }
  1.3515 +              if (nbrepeats && position>=command_line.size)
  1.3516 +                error("Directive '-done' is missing after a '-repeat' command.");
  1.3517 +              continue;
  1.3518 +            }
  1.3519 +          } else error("Repeat operation : Invalid argument '%s' "
  1.3520 +                       "(should be a number).",argument_text);
  1.3521 +          ++position; continue;
  1.3522 +        }
  1.3523 +
  1.3524 +        if (!cimg::strcmp("-done",item)) {
  1.3525 +          if (!repeatdone) error("Directive '-done' is not associated with a '-repeat' command.");
  1.3526 +          if (--repeatdone.last()(1))
  1.3527 +            position = (unsigned int)repeatdone.last()(0);
  1.3528 +          else repeatdone.remove();
  1.3529 +          continue;
  1.3530 +        }
  1.3531 +
  1.3532 +        // Check argument type
  1.3533 +        if (!cimg::strcmp("-int",item)) {
  1.3534 +          char it[4096], end = 0, sep = 0; int value = 0;
  1.3535 +          if (*argument) for (const char *nargument = argument; *nargument; ) {
  1.3536 +            const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
  1.3537 +            if (nb) {
  1.3538 +              if (std::sscanf(it,"%d%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
  1.3539 +              else error("Argument '%s' is not an integer value.",it);
  1.3540 +            } else error("Argument '%s' is not an integer value.",argument_text);
  1.3541 +          }
  1.3542 +          ++position; continue;
  1.3543 +        }
  1.3544 +
  1.3545 +        if (!cimg::strcmp("-float",item)) {
  1.3546 +          char it[4096], end = 0, sep = 0; double value = 0;
  1.3547 +          if (*argument) for (const char *nargument = argument; *nargument; ) {
  1.3548 +            const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
  1.3549 +            if (nb) {
  1.3550 +              if (std::sscanf(it,"%lf%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
  1.3551 +              else error("Argument '%s' is not a float value.",it);
  1.3552 +            } else error("Argument '%s' is not a float value.",argument_text);
  1.3553 +          }
  1.3554 +          ++position; continue;
  1.3555 +        }
  1.3556 +
  1.3557 +        //--------------------------
  1.3558 +        // Input/Output and Display
  1.3559 +        //--------------------------
  1.3560 +
  1.3561 +        // Display.
  1.3562 +        if (!cimg::strcmp("-display",item0) || !cimg::strcmp("-d",item0)) {
  1.3563 +          if (display_images(images,indices,true)) is_released = true;
  1.3564 +          continue;
  1.3565 +        }
  1.3566 +
  1.3567 +        // Display 3D object.
  1.3568 +        if (!cimg::strcmp("-display3d",item0) || !cimg::strcmp("-d3d",item0)) {
  1.3569 +          if (display_objects3d(images,indices,true)) is_released = true;
  1.3570 +          continue;
  1.3571 +        }
  1.3572 +
  1.3573 +        // Display as a graph plot.
  1.3574 +        if (!cimg::strcmp("-plot",item0)) {
  1.3575 +          int plot_type = 1, vertex_type = 1; double ymin = 0, ymax = 0, xmin = 0, xmax = 0; char end = 0;
  1.3576 +          const int nb = std::sscanf(argument,"%d%*c%d%*c%lf%*c%lf%*c%lf%*c%lf%c",&plot_type,&vertex_type,&xmin,&xmax,&ymin,&ymax,&end);
  1.3577 +          if (nb==1 || nb==2 || nb==4 || nb==6) ++position;
  1.3578 +          else { plot_type = 1; vertex_type = 0; ymin = ymax = xmin = xmax = 0; }
  1.3579 +          is_released |= display_plots(images,indices,plot_type,vertex_type,xmin,xmax,ymin,ymax,true);
  1.3580 +          continue;
  1.3581 +        }
  1.3582 +
  1.3583 +        // Select image feature.
  1.3584 +        if (!cimg::strcmp("-select",item0)) {
  1.3585 +          int select_type = 0; char end = 0;
  1.3586 +          if (std::sscanf(argument,"%d%c",&select_type,&end)==1) {
  1.3587 +            cimg_foroff(indices,l) gmic_apply(images[indices[l]],select(filenames[indices[l]].ptr(),select_type));
  1.3588 +          } else error("Select image%s : Invalid argument '%s' "
  1.3589 +                       "(should be 'select_type').",gmic_inds,argument_text);
  1.3590 +          ++position; continue;
  1.3591 +        }
  1.3592 +
  1.3593 +        // Output.
  1.3594 +        if (!cimg::strcmp("-output",item0) || !cimg::strcmp("-o",item0)) {
  1.3595 +          char filename[4096] = { 0 }; char options[4096] = { 0 };
  1.3596 +          if (std::sscanf(argument,"%4095[^,],%s",filename,options)!=2) std::strcpy(filename,argument);
  1.3597 +          const char *const ext = cimg::split_filename(filename);
  1.3598 +          if (!cimg::strcasecmp("off",ext)) {
  1.3599 +            char nfilename[4096] = { 0 };
  1.3600 +            std::strcpy(nfilename,filename);
  1.3601 +            const unsigned int siz = indices.size();
  1.3602 +            cimg_foroff(indices,l) {
  1.3603 +              const unsigned int ind = indices[l];
  1.3604 +              if (siz!=1) cimg::number_filename(filename,l,6,nfilename);
  1.3605 +              if (!images[ind].is_CImg3d())
  1.3606 +                error("Output 3D object [%u] as file '%s' : Image [%u] is not a 3D object.",ind,nfilename,ind);
  1.3607 +              print("Output 3D object [%u] as file '%s'.",ind,nfilename);
  1.3608 +              CImgList<unsigned int> primitives3d;
  1.3609 +              CImgList<unsigned char> colors3d;
  1.3610 +              CImg<float> opacities3d;
  1.3611 +              CImg<float> points3d(images[ind]);
  1.3612 +              points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d).save_off(nfilename,primitives3d,colors3d);
  1.3613 +            }
  1.3614 +          } else if (!cimg::strcasecmp("jpeg",ext) || !cimg::strcasecmp("jpg",ext)) {
  1.3615 +            int quality = 100; char end = 0;
  1.3616 +            if (std::sscanf(options,"%d%c",&quality,&end)!=1) quality = 100;
  1.3617 +            if (quality<0) quality = 0; else if (quality>100) quality = 100;
  1.3618 +            CImgList<T> output_images;
  1.3619 +            cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
  1.3620 +            print("Output image%s as file '%s', with quality %u%%",gmic_inds,filename,quality);
  1.3621 +            if (!output_images) throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.",
  1.3622 +                                                            output_images.pixel_type(),filename,
  1.3623 +                                                            output_images.size,output_images.data);
  1.3624 +            if (output_images.size==1) output_images[0].save_jpeg(filename,quality);
  1.3625 +            else {
  1.3626 +              char nfilename[1024];
  1.3627 +              cimglist_for(output_images,l) {
  1.3628 +                cimg::number_filename(filename,l,6,nfilename);
  1.3629 +                output_images[l].save_jpeg(nfilename,quality);
  1.3630 +              }
  1.3631 +            }
  1.3632 +          } else {
  1.3633 +            CImgList<T> output_images;
  1.3634 +            cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
  1.3635 +            print("Output image%s as file '%s'.",gmic_inds,filename);
  1.3636 +            output_images.save(filename);
  1.3637 +          }
  1.3638 +          is_released = true; ++position; continue;
  1.3639 +        }
  1.3640 +
  1.3641 +        // Substitute macros commands if necessary.
  1.3642 +        if (cimg::strcmp("-i",item0) && cimg::strcmp("-input",item0)) {
  1.3643 +          bool macro_found = false;
  1.3644 +          cimglist_for(macros,l) {
  1.3645 +            const char
  1.3646 +              *const macro = macros[l].ptr(),
  1.3647 +              *const command = commands[l].ptr();
  1.3648 +
  1.3649 +            if (!cimg::strcmp(item+1,macro) && *command) {
  1.3650 +              CImgList<char> arguments(256);
  1.3651 +              unsigned int nb_arguments = 0;
  1.3652 +              char s_argument[4096] = { 0 }, tmp[4096] = { 0 }, tmp2[4096] = { 0 };
  1.3653 +              bool has_arguments = false;
  1.3654 +              macro_found = true;
  1.3655 +              debug("Found macro '%s', substituting by '%s'.",macro,command);
  1.3656 +
  1.3657 +              // Get command-line values of macro arguments.
  1.3658 +              if (argument)
  1.3659 +                for (const char *nargument = argument; nb_arguments<256 && *nargument &&
  1.3660 +                       std::sscanf(nargument,"%4095[^,]",s_argument)==1;) {
  1.3661 +                  CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[nb_arguments++]);
  1.3662 +                  nargument+=cimg::strlen(s_argument);
  1.3663 +                  if (*nargument) ++nargument;
  1.3664 +                }
  1.3665 +
  1.3666 +              // Substitute arguments in macro command expression.
  1.3667 +              CImg<char> substituted_command;
  1.3668 +              CImgList<char> lreplacement;
  1.3669 +              for (const char *ncommand = command; *ncommand;) if (*ncommand=='$') {
  1.3670 +                char *replace_text = 0, sep = 0;
  1.3671 +                int ind = 0, ind1 = 0;
  1.3672 +
  1.3673 +                // Replace $# and ${#}.
  1.3674 +                if (ncommand[1]=='#' || (ncommand[1]=='{' && ncommand[2]=='#' && ncommand[3]=='}')) {
  1.3675 +                  std::sprintf(replace_text=s_argument,"%u",nb_arguments);
  1.3676 +                  ncommand+=(ncommand[1]=='#')?2:4;
  1.3677 +                  has_arguments = true;
  1.3678 +
  1.3679 +                  // Replace $* and ${*}.
  1.3680 +                } else if (ncommand[1]=='*' || (ncommand[1]=='{' && ncommand[2]=='*' && ncommand[3]=='}')) {
  1.3681 +                  replace_text = &(s_argument[0]=0);
  1.3682 +                  for (unsigned int j = 1; j<=nb_arguments; ++j) {
  1.3683 +                    replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
  1.3684 +                    if (j<nb_arguments) *(replace_text++) = ',';
  1.3685 +                  }
  1.3686 +                  replace_text = s_argument;
  1.3687 +                  ncommand+=(ncommand[1]=='*')?2:4;
  1.3688 +                  has_arguments = true;
  1.3689 +
  1.3690 +                  // Replace ${i*}.
  1.3691 +                } else if (std::sscanf(ncommand,"${%d*%c",&ind,&sep)==2 &&
  1.3692 +                           ind>0 && ind<256 && sep=='}') {
  1.3693 +                  replace_text = &(s_argument[0]=0);
  1.3694 +                  for (unsigned int j = ind; j<=nb_arguments; ++j) {
  1.3695 +                    replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
  1.3696 +                    if (j<nb_arguments) *(replace_text++) = ',';
  1.3697 +                  }
  1.3698 +                  replace_text = s_argument;
  1.3699 +                  ncommand+=std::sprintf(tmp,"${%d*}",ind);
  1.3700 +                  has_arguments = true;
  1.3701 +
  1.3702 +                  // Replace $i and ${i}.
  1.3703 +                } else if ((std::sscanf(ncommand,"$%d",&ind)==1 ||
  1.3704 +                            (std::sscanf(ncommand,"${%d%c",&ind,&sep)==2 && sep=='}')) &&
  1.3705 +                           ind>0 && ind<256) {
  1.3706 +                  if (!arguments[ind-1]) {
  1.3707 +                    if (sep=='}') error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d}').",macro,ind,ind);
  1.3708 +                    else error("Macro '%s' : Argument '$%d' is undefined (in expression '$%d').",macro,ind,ind);
  1.3709 +                  }
  1.3710 +                  replace_text = arguments[ind-1].ptr();
  1.3711 +                  ncommand+=std::sprintf(tmp,"$%d",ind) + (sep=='}'?2:0);
  1.3712 +                  has_arguments = true;
  1.3713 +
  1.3714 +                  // Replace ${i=$#}.
  1.3715 +                } else if (std::sscanf(ncommand,"${%d=$#%c",&ind,&sep)==2 &&
  1.3716 +                           ind>0 && ind<256 && sep=='}') {
  1.3717 +                  std::sprintf(replace_text=s_argument,"%g",(double)nb_arguments);
  1.3718 +                  CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[ind-1]);
  1.3719 +                  ncommand+=std::sprintf(tmp,"${%d=$#}",ind);
  1.3720 +                  has_arguments = true;
  1.3721 +
  1.3722 +                  // Replace ${i=$j}.
  1.3723 +                } else if (std::sscanf(ncommand,"${%d=$%d%c",&ind,&ind1,&sep)==3 && sep=='}' &&
  1.3724 +                           ind>0 && ind<256 && ind1>0 && ind1<256) {
  1.3725 +                  if (!arguments[ind1-1])
  1.3726 +                    error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d=$%d}').",macro,ind1,ind,ind1);
  1.3727 +                  if (!arguments[ind-1]) arguments[ind-1] = arguments[ind1-1];
  1.3728 +                  replace_text = arguments[ind-1].ptr();
  1.3729 +                  ncommand+=std::sprintf(tmp,"${%d=$%d}",ind,ind1);
  1.3730 +                  has_arguments = true;
  1.3731 +
  1.3732 +                  // Replace ${i=default}.
  1.3733 +                } else if (std::sscanf(ncommand,"${%d=%4095[^}]%c",&ind,tmp,&sep)==3 && sep=='}' &&
  1.3734 +                           ind>0 && ind<256) {
  1.3735 +                  if (!arguments[ind-1]) CImg<char>(tmp,cimg::strlen(tmp)+1,1,1,1,false).transfer_to(arguments[ind-1]);
  1.3736 +                  replace_text = arguments[ind-1].ptr();
  1.3737 +                  ncommand+=cimg::strlen(tmp) + 4 + std::sprintf(tmp2,"%d",ind);
  1.3738 +                  has_arguments = true;
  1.3739 +
  1.3740 +                  // Any other expression starting by '$'.
  1.3741 +                } else {
  1.3742 +                  replace_text = &(s_argument[0]='$');
  1.3743 +                  if (std::sscanf(ncommand,"%4095[^$]",s_argument+1)!=1) { s_argument[1] = 0; ++ncommand; }
  1.3744 +                  else ncommand+=cimg::strlen(s_argument);
  1.3745 +                }
  1.3746 +
  1.3747 +                const int replace_length = cimg::strlen(replace_text);
  1.3748 +                if (replace_length) {
  1.3749 +                  lreplacement.insert(1);
  1.3750 +                  CImg<char>(replace_text,replace_length,1,1,1,false).transfer_to(lreplacement.last());
  1.3751 +                }
  1.3752 +
  1.3753 +              } else {
  1.3754 +                std::sscanf(ncommand,"%4095[^$]",s_argument);
  1.3755 +                const int replace_length = cimg::strlen(s_argument);
  1.3756 +                if (replace_length) {
  1.3757 +                  lreplacement.insert(1);
  1.3758 +                  CImg<char>(s_argument,replace_length,1,1,1,false).transfer_to(lreplacement.last());
  1.3759 +                  ncommand+=cimg::strlen(s_argument);
  1.3760 +                }
  1.3761 +              }
  1.3762 +              const CImg<char> zero(1,1,1,1,0);
  1.3763 +              lreplacement.insert(zero).get_append('x').transfer_to(substituted_command);
  1.3764 +
  1.3765 +              // Substitute macro expression in command line.
  1.3766 +              bool is_dquote = false;
  1.3767 +              cimg_foroff(substituted_command,k)
  1.3768 +                if (substituted_command[k]=='"') is_dquote = !is_dquote;
  1.3769 +                else if (is_dquote && substituted_command[k]==' ') substituted_command[k] = 30;
  1.3770 +              CImgList<char> command_items = substituted_command.get_split(' ',false,false);
  1.3771 +              cimglist_for(command_items,k) {
  1.3772 +                CImg<char> &item = command_items[k];
  1.3773 +                cimg_foroff(item,l) if (item[l]==30) item[l]=' ';
  1.3774 +              }
  1.3775 +              cimglist_for(command_items,k) command_items[k].append(zero,'y');
  1.3776 +              if (position<command_line.size && has_arguments) command_line.remove(position);
  1.3777 +              command_line.remove(--position);
  1.3778 +              command_line.insert(command_items,position);
  1.3779 +              break;
  1.3780 +            }
  1.3781 +          }
  1.3782 +          if (macro_found) continue;
  1.3783 +        }
  1.3784 +      }
  1.3785 +
  1.3786 +      // Input.
  1.3787 +      if (!cimg::strcmp("-i",item0) || !cimg::strcmp("-input",item0)) ++position;
  1.3788 +      else { if (get_version) --item; argument = item; item1[0] = 0; }
  1.3789 +      if (!cimg::strlen(item1)) indices.assign(1,1,1,1,images.size);
  1.3790 +      CImgList<T> input_images;
  1.3791 +      CImgList<char> input_filenames;
  1.3792 +      bool obj3d = false;
  1.3793 +      char st_inds[4096] = { 0 }, stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
  1.3794 +      char end = 0, sep = 0, sepx = 0, sepy = 0, sepz = 0, sepv = 0;
  1.3795 +      int nb = 1, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
  1.3796 +      float dx = 0, dy = 1, dz = 1, dv = 1;
  1.3797 +
  1.3798 +      if (std::sscanf(argument,"[%4095[0-9%,:-]]%*c%d%c",st_inds,&nb,&end)==2 ||
  1.3799 +          (std::sscanf(argument,"[%4095[0-9%,:-]%c%c",st_inds,&sep,&end)==2 && sep==']')) {
  1.3800 +
  1.3801 +        // nb copies of existing sub-images.
  1.3802 +        const CImg<unsigned int> indices0 = indices2cimg(st_inds,images.size,"-input");
  1.3803 +        char st_tmp[4096] = { 0 }; std::strcpy(st_tmp,indices2string(indices0,true));
  1.3804 +        if (nb<=0) error("Input %d copies of image%s : Invalid argument '%s'.",
  1.3805 +                         nb,st_tmp,argument_text);
  1.3806 +        if (nb!=1) print("Input %d copies of image%s at position%s",nb,st_tmp,gmic_inds);
  1.3807 +        else print("Input copy of image%s at position%s",st_tmp,gmic_inds);
  1.3808 +        for (int i = 0; i<nb; ++i) cimg_foroff(indices0,l) {
  1.3809 +          input_images.insert(images[indices0[l]]);
  1.3810 +          input_filenames.insert(filenames[indices0[l]]);
  1.3811 +        }
  1.3812 +      } else if (((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%c",
  1.3813 +                               stx,sty,stz,stv,&(nb=1),&end)==5 ||
  1.3814 +                   std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1.3815 +                               stx,sty,stz,stv,&end)==4) &&
  1.3816 +                  (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  1.3817 +                   (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  1.3818 +                   (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  1.3819 +                  (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  1.3820 +                   (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  1.3821 +                   (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
  1.3822 +                  (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
  1.3823 +                   (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
  1.3824 +                   (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']')) &&
  1.3825 +                  (std::sscanf(stv,"%f%c",&dv,&end)==1 ||
  1.3826 +                   (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%') ||
  1.3827 +                   (std::sscanf(stv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']'))) ||
  1.3828 +                 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
  1.3829 +                  (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  1.3830 +                   (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  1.3831 +                   (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  1.3832 +                  (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  1.3833 +                   (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  1.3834 +                   (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
  1.3835 +                  (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
  1.3836 +                   (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
  1.3837 +                   (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']'))) ||
  1.3838 +                 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,&end)==2 &&
  1.3839 +                  (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  1.3840 +                   (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  1.3841 +                   (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  1.3842 +                  (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  1.3843 +                   (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  1.3844 +                   (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']'))) ||
  1.3845 +                 (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",stx,&end)==1 &&
  1.3846 +                  (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  1.3847 +                   (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  1.3848 +                   (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')))) {
  1.3849 +
  1.3850 +        // nb new black image.
  1.3851 +        if (indx!=no_ind) { gmic_check_indice(indx,"Input black image%s"); dx = (float)images[indx].dimx(); sepx = 0; }
  1.3852 +        if (indy!=no_ind) { gmic_check_indice(indy,"Input black image%s"); dy = (float)images[indy].dimy(); sepy = 0; }
  1.3853 +        if (indz!=no_ind) { gmic_check_indice(indz,"Input black image%s"); dz = (float)images[indz].dimz(); sepz = 0; }
  1.3854 +        if (indv!=no_ind) { gmic_check_indice(indv,"Input black image%s"); dv = (float)images[indv].dimv(); sepv = 0; }
  1.3855 +        if (sepx=='%') { dx = images.size?dx*images.last().dimx()/100:0; if (!(int)dx) ++dx; }
  1.3856 +        if (sepy=='%') { dy = images.size?dy*images.last().dimy()/100:0; if (!(int)dy) ++dy; }
  1.3857 +        if (sepz=='%') { dz = images.size?dz*images.last().dimz()/100:0; if (!(int)dz) ++dz; }
  1.3858 +        if (sepv=='%') { dv = images.size?dv*images.last().dimv()/100:0; if (!(int)dv) ++dv; }
  1.3859 +
  1.3860 +        if (nb<=0) error("Input %d black image%s : Invalid number of copies.",nb,gmic_inds);
  1.3861 +        if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
  1.3862 +          error("Input %d black image%s : Invalid image dimensions %gx%gx%gx%g.",
  1.3863 +                nb,gmic_inds,dx,dy,dz,dv);
  1.3864 +        if (nb!=1) print("Input %d black images at position%s",nb,gmic_inds);
  1.3865 +        else print("Input black image at position%s",gmic_inds);
  1.3866 +        CImg<T> empty((int)dx,(int)dy,(int)dz,(int)dv,0);
  1.3867 +        input_images.insert(nb-1,empty); input_images.insert(1);
  1.3868 +        input_images.last().swap(empty);
  1.3869 +        filenames.insert(input_images.size,CImg<char>("(gmic)",7,1,1,1,false));
  1.3870 +      } else if (std::sscanf(argument,"(%4095[^)])x%d%c",stx,&(nb=1),&end)==2 ||
  1.3871 +                 (std::sscanf(argument,"(%4095[^)]%c%c",stx,&sep,&end)==2 && sep==')')) {
  1.3872 +
  1.3873 +        // Insert nb IxJxKxL image(s) with specified values.
  1.3874 +        if (nb<=0) error("Input %d images : Invalid number of copies.",nb);
  1.3875 +        unsigned int cx = 0, cy = 0, cz = 0, cv = 0, maxcx = 0, maxcy = 0, maxcz = 0;
  1.3876 +        const char *nargument = 0;
  1.3877 +        for (nargument = argument+1; *nargument!=')'; ) {
  1.3878 +          char s_value[256] = { 0 }, separator = 0; double value = 0;
  1.3879 +          if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
  1.3880 +            if (cx>maxcx) maxcx = cx;
  1.3881 +            if (cy>maxcy) maxcy = cy;
  1.3882 +            if (cz>maxcz) maxcz = cz;
  1.3883 +            switch (separator) {
  1.3884 +            case '^' : cx = cy = cz = 0; ++cv; break;
  1.3885 +            case '/' : cx = cy = 0; ++cz; break;
  1.3886 +            case ';' : cx = 0; ++cy; break;
  1.3887 +            default : ++cx;
  1.3888 +            }
  1.3889 +            nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
  1.3890 +          } else break;
  1.3891 +        }
  1.3892 +        if (*nargument!=')') error("Input %d images : Invalid input string '%s'.",nb,argument);
  1.3893 +
  1.3894 +        CImg<T> img(maxcx+1,maxcy+1,maxcz+1,cv+1,0);
  1.3895 +        cx = cy = cz = cv = 0;
  1.3896 +        for (nargument = argument+1; *nargument; ) {
  1.3897 +          char s_value[256] = { 0 }, separator = 0; double value = 0;
  1.3898 +          if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
  1.3899 +            img(cx,cy,cz,cv) = (T)value;
  1.3900 +            switch (separator) {
  1.3901 +            case '^' : cx = cy = cz = 0; ++cv; break;
  1.3902 +            case '/' : cx = cy = 0; ++cz; break;
  1.3903 +            case ';' : cx = 0; ++cy; break;
  1.3904 +            default : ++cx;
  1.3905 +            }
  1.3906 +            nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
  1.3907 +          } else break;
  1.3908 +        }
  1.3909 +        if (nb==1) print("Input image %dx%dx%dx%d",img.dimx(),img.dimy(),img.dimz(),img.dimv());
  1.3910 +        else print("Input %d images %dx%d",nb,img.dimx(),img.dimy(),img.dimz(),img.dimv());
  1.3911 +        input_images.insert(nb,img); filenames.insert(nb,CImg<char>("(gmic)",7,1,1,1,false));
  1.3912 +      } else {
  1.3913 +
  1.3914 +        // Insert image as a loaded filename.
  1.3915 +        char filename[4096] = { 0 }, options[4096] = { 0 }; const char *ext = 0, *basename = 0;
  1.3916 +        if (argument[0]!='-' || (argument[1] && argument[1]!='.')) {
  1.3917 +          std::FILE *file = std::fopen(argument,"r");
  1.3918 +          if (file) { std::fclose(file); std::strcpy(filename,argument); }
  1.3919 +          else {
  1.3920 +            std::sscanf(argument,"%4095[^,],%s",filename,options);
  1.3921 +            if (!(file=std::fopen(filename,"r"))) {
  1.3922 +              if (filename[0]=='-') error("Input '%s' : Command not found.",filename);
  1.3923 +              else error("Input '%s' : File not found.",filename);
  1.3924 +            }
  1.3925 +            std::fclose(file);
  1.3926 +          }
  1.3927 +        } else std::strcpy(filename,argument);
  1.3928 +        basename = cimg::basename(filename);
  1.3929 +        ext = cimg::split_filename(filename);
  1.3930 +
  1.3931 +        if (!cimg::strcasecmp("off",ext)) {  // 3D object file.
  1.3932 +          print("Input 3D object '%s'",filename);
  1.3933 +          CImgList<unsigned int> primitives3d;
  1.3934 +          CImgList<unsigned char> colors3d;
  1.3935 +          CImg<float> opacities3d, points3d = CImg<float>::get_load_off(filename,primitives3d,colors3d);
  1.3936 +          opacities3d.assign(1,primitives3d.size,1,1,1);
  1.3937 +          points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  1.3938 +          input_images.insert(1); points3d.transfer_to(input_images[0]);
  1.3939 +          input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
  1.3940 +                                            cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  1.3941 +          obj3d = true;
  1.3942 +        } else if (!cimg::strcasecmp(ext,"avi") ||
  1.3943 +                   !cimg::strcasecmp(ext,"mov") ||
  1.3944 +                   !cimg::strcasecmp(ext,"asf") ||
  1.3945 +                   !cimg::strcasecmp(ext,"divx") ||
  1.3946 +                   !cimg::strcasecmp(ext,"flv") ||
  1.3947 +                   !cimg::strcasecmp(ext,"mpg") ||
  1.3948 +                   !cimg::strcasecmp(ext,"m1v") ||
  1.3949 +                   !cimg::strcasecmp(ext,"m2v") ||
  1.3950 +                   !cimg::strcasecmp(ext,"m4v") ||
  1.3951 +                   !cimg::strcasecmp(ext,"mjp") ||
  1.3952 +                   !cimg::strcasecmp(ext,"mkv") ||
  1.3953 +                   !cimg::strcasecmp(ext,"mpe") ||
  1.3954 +                   !cimg::strcasecmp(ext,"movie") ||
  1.3955 +                   !cimg::strcasecmp(ext,"ogm") ||
  1.3956 +                   !cimg::strcasecmp(ext,"qt") ||
  1.3957 +                   !cimg::strcasecmp(ext,"rm") ||
  1.3958 +                   !cimg::strcasecmp(ext,"vob") ||
  1.3959 +                   !cimg::strcasecmp(ext,"wmv") ||
  1.3960 +                   !cimg::strcasecmp(ext,"xvid") ||
  1.3961 +                   !cimg::strcasecmp(ext,"mpeg")) {
  1.3962 +          unsigned int value0 = 0, value1 = 0, step = 1; char sep0 = 0, sep1 = 0, end = 0;
  1.3963 +          if ((std::sscanf(options,"%u%c%*c%u%c%*c%u%c",&value0,&sep0,&value1,&sep1,&step,&end)==5 && sep0=='%' && sep1=='%') ||
  1.3964 +              (std::sscanf(options,"%u%c%*c%u%*c%u%c",&value0,&sep0,&value1,&step,&end)==4 && sep0=='%') ||
  1.3965 +              (std::sscanf(options,"%u%*c%u%c%*c%u%c",&value0,&value1,&sep1,&step,&end)==4 && sep1=='%') ||
  1.3966 +              (std::sscanf(options,"%u%*c%u%*c%u%c",&value0,&value1,&step,&end)==3) ||
  1.3967 +              (std::sscanf(options,"%u%c%*c%u%c%c",&value0,&sep0,&value1,&sep1,&end)==4 && sep0=='%' && sep1=='%') ||
  1.3968 +              (std::sscanf(options,"%u%c%*c%u%c",&value0,&sep0,&value1,&end)==3 && sep0=='%') ||
  1.3969 +              (std::sscanf(options,"%u%*c%u%c%c",&value0,&value1,&sep1,&end)==3 && sep1=='%') ||
  1.3970 +              (std::sscanf(options,"%u%*c%u%c",&value0,&value1,&end)==2)) { // Read several frames
  1.3971 +            print("Input frames %u%s...%u%s with step %u of file '%s'",
  1.3972 +                  value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",step,filename);
  1.3973 +            if (sep0=='%' || sep1=='%') {
  1.3974 +              const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
  1.3975 +              if (sep0=='%') value0 = value0*nb_frames/100;
  1.3976 +              if (sep1=='%') value1 = value1*nb_frames/100;
  1.3977 +            }
  1.3978 +          } else if ((std::sscanf(options,"%u%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1.3979 +                     (std::sscanf(options,"%u%c",&value0,&end)==1)) { // Read one frame
  1.3980 +            print("Input frame %u%s of file '%s'",value0,sep0=='%'?"%":"",filename);
  1.3981 +            if (sep0=='%') {
  1.3982 +              const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
  1.3983 +              value0 = value0*nb_frames/100;
  1.3984 +            }
  1.3985 +            value1 = value0; step = 1;
  1.3986 +          } else { // Read all frames
  1.3987 +            print("Input all frames of file '%s'",filename);
  1.3988 +            value0 = 0; value1 = ~0U; sep0 = sep1 = 0; step = 1;
  1.3989 +          }
  1.3990 +          input_images.load_ffmpeg(filename,value0,value1,step);
  1.3991 +          if (input_images)
  1.3992 +            input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
  1.3993 +                                                                cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  1.3994 +        } else if (!cimg::strcasecmp("raw",ext)) { // Raw file.
  1.3995 +          int dx = 0, dy = 1, dz = 1, dv = 1;
  1.3996 +          if (std::sscanf(options,"%d%*c%d%*c%d%*c%d",&dx,&dy,&dz,&dv)>0) {
  1.3997 +            if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
  1.3998 +              error("Input raw file '%s' : Invalid specified dimensions %dx%dx%dx%d.",filename,dx,dy,dz,dv);
  1.3999 +            print("Input raw file '%s'",filename);
  1.4000 +            input_images.insert(1); input_images[0].load_raw(filename,dx,dy,dz,dv);
  1.4001 +            input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
  1.4002 +                                              cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  1.4003 +          } else error("Input raw file '%s' : Image dimensions must be specified.",filename);
  1.4004 +        } else if (!cimg::strcasecmp("yuv",ext)) { // YUV file.
  1.4005 +          int dx = 0, dy = 0; unsigned int first = 0, last = ~0U, step = 1;
  1.4006 +          if (std::sscanf(options,"%d%*c%d%*c%u%*c%u%*c%u",&dx,&dy,&first,&last,&step)>0) {
  1.4007 +            if (dx<=0 || dy<=0)
  1.4008 +              error("Input yuv file '%s' : Invalid specified dimensions %dx%d.",filename,dx,dy);
  1.4009 +            print("Input yuv file '%s'",filename);
  1.4010 +            input_images.load_yuv(filename,dx,dy,first,last,step);
  1.4011 +            input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
  1.4012 +                                                                cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  1.4013 +          } else error("Input yuv file '%s' : Image dimensions must be specified.",filename);
  1.4014 +        } else { // Other file type.
  1.4015 +          print("Input file '%s'",filename);
  1.4016 +          input_images.load(filename);
  1.4017 +          input_filenames.insert(input_images.size,
  1.4018 +                                 CImg<char>(is_fullpath?filename:basename,
  1.4019 +                                            cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  1.4020 +        }
  1.4021 +      }
  1.4022 +
  1.4023 +      if (verbosity_level>=0) {
  1.4024 +        if (input_images) {
  1.4025 +          const unsigned int last = input_images.size-1;
  1.4026 +          if (obj3d)
  1.4027 +            std::fprintf(cimg_stdout," (%d points, %u primitives, %u colors).",
  1.4028 +                         (unsigned int)input_images(0,6),
  1.4029 +                         (unsigned int)input_images(0,7),
  1.4030 +                         (unsigned int)input_images(0,8));
  1.4031 +          else if (input_images.size==1)
  1.4032 +            std::fprintf(cimg_stdout," (1 image %ux%ux%ux%u).",
  1.4033 +                         input_images[0].width,input_images[0].height,input_images[0].depth,
  1.4034 +                         input_images[0].dim);
  1.4035 +          else std::fprintf(cimg_stdout," (%u images [0] = %ux%ux%ux%u, %s[%u] = %ux%ux%ux%u).",
  1.4036 +                            input_images.size,
  1.4037 +                            input_images[0].width,input_images[0].height,input_images[0].depth,
  1.4038 +                            input_images[0].dim,
  1.4039 +                            last==1?"":"...,",
  1.4040 +                            last,
  1.4041 +                            input_images[last].width,input_images[last].height,input_images[last].depth,
  1.4042 +                            input_images[last].dim);
  1.4043 +        } else std::fprintf(cimg_stdout," (no available data).");
  1.4044 +      }
  1.4045 +
  1.4046 +      for (unsigned int l = 0, siz = indices.size()-1, off = 0; l<=siz; ++l) {
  1.4047 +        const unsigned int ind = indices[l] + off;
  1.4048 +        if (l!=siz) images.insert(input_images,ind);
  1.4049 +        else {
  1.4050 +          images.insert(input_images.size,ind);
  1.4051 +          cimglist_for(input_images,k) images[ind+k].swap(input_images[k]);
  1.4052 +        }
  1.4053 +        filenames.insert(input_filenames,ind);
  1.4054 +        off+=input_images.size;
  1.4055 +      }
  1.4056 +
  1.4057 +    } catch (CImgException &e) {
  1.4058 +      const char *error_message = e.message;
  1.4059 +      char tmp[4096] = { 0 }, sep = 0;
  1.4060 +      if (std::sscanf(error_message,"%4095[^>]>:%c",tmp,&sep)==2 && sep==':') error_message+=cimg::strlen(tmp)+3;
  1.4061 +      error(error_message);
  1.4062 +    }
  1.4063 +  }
  1.4064 +
  1.4065 +  // Check if command line has grown too much (possible recursive macro calls).
  1.4066 +  if (command_line.size>=command_line_maxsize)
  1.4067 +    error("Command line overflow : There are too much commands specified (possible recursive macro substitution).");
  1.4068 +
  1.4069 +  // Check if some loops have not been terminated.
  1.4070 +  if (dowhile) warning("A '-while' directive is missing somewhere.");
  1.4071 +  if (repeatdone) warning("A '-done' directive is missing somewhere.");
  1.4072 +
  1.4073 +  // Display final result if necessary (not 'released' before).
  1.4074 +  if (images.size && !is_released) {
  1.4075 +    if (!display_objects3d(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),false))
  1.4076 +      display_images(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),true);
  1.4077 +  }
  1.4078 +
  1.4079 +  print("End G'MIC instance.\n");
  1.4080 +  return *this;
  1.4081 +}
  1.4082 +
  1.4083 +// Small hack to separate the compilation of G'MIC in different pixel types.
  1.4084 +// (only intended to save computer memory when compiling !)
  1.4085 +//--------------------------------------------------------------------------
  1.4086 +#ifdef gmic_minimal
  1.4087 +gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
  1.4088 +template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4089 +template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4090 +#else
  1.4091 +#if defined(gmic_bool) || !defined(gmic_separate_compilation)
  1.4092 +gmic& gmic::parse_bool(CImgList<bool>& images) { return parse(images); }
  1.4093 +template gmic::gmic(const int, const char *const *const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4094 +template gmic::gmic(const char* const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4095 +#endif
  1.4096 +#if defined(gmic_uchar) || !defined(gmic_separate_compilation)
  1.4097 +gmic& gmic::parse_uchar(CImgList<unsigned char>& images) { return parse(images); }
  1.4098 +template gmic::gmic(const int, const char *const *const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4099 +template gmic::gmic(const char* const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4100 +#endif
  1.4101 +#if defined(gmic_char) || !defined(gmic_separate_compilation)
  1.4102 +gmic& gmic::parse_char(CImgList<char>& images) { return parse(images); }
  1.4103 +template gmic::gmic(const int, const char *const *const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4104 +template gmic::gmic(const char* const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4105 +#endif
  1.4106 +#if defined(gmic_ushort) || !defined(gmic_separate_compilation)
  1.4107 +gmic& gmic::parse_ushort(CImgList<unsigned short>& images) { return parse(images); }
  1.4108 +template gmic::gmic(const int, const char *const *const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4109 +template gmic::gmic(const char* const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4110 +#endif
  1.4111 +#if defined(gmic_short) || !defined(gmic_separate_compilation)
  1.4112 +gmic& gmic::parse_short(CImgList<short>& images) { return parse(images); }
  1.4113 +template gmic::gmic(const int, const char *const *const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4114 +template gmic::gmic(const char* const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4115 +#endif
  1.4116 +#if defined(gmic_uint) || !defined(gmic_separate_compilation)
  1.4117 +gmic& gmic::parse_uint(CImgList<unsigned int>& images) { return parse(images); }
  1.4118 +template gmic::gmic(const int, const char *const *const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4119 +template gmic::gmic(const char* const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4120 +#endif
  1.4121 +#if defined(gmic_int) || !defined(gmic_separate_compilation)
  1.4122 +gmic& gmic::parse_int(CImgList<int>& images) { return parse(images); }
  1.4123 +template gmic::gmic(const int, const char *const *const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4124 +template gmic::gmic(const char* const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4125 +#endif
  1.4126 +#if defined(gmic_float) || !defined(gmic_separate_compilation)
  1.4127 +gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
  1.4128 +template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4129 +template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4130 +#endif
  1.4131 +#if defined(gmic_double) || !defined(gmic_separate_compilation)
  1.4132 +gmic& gmic::parse_double(CImgList<double>& images) { return parse(images); }
  1.4133 +template gmic::gmic(const int, const char *const *const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4134 +template gmic::gmic(const char* const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
  1.4135 +#endif
  1.4136 +#endif
  1.4137 +#endif
  1.4138 +
  1.4139 +//-----------------------
  1.4140 +// Start main procedure.
  1.4141 +//-----------------------
  1.4142 +#if defined(gmic_main) || (!defined(gmic_separate_compilation) && !defined(gmic_minimal))
  1.4143 +extern char data_def[];
  1.4144 +
  1.4145 +int main(int argc, char **argv) {
  1.4146 +
  1.4147 +  // Display help if necessary.
  1.4148 +  //---------------------------
  1.4149 +  if (argc==1) {
  1.4150 +    std::fprintf(cimg_stdout,"<gmic> No options or data provided. Try '%s -h' for help.\n",cimg::basename(argv[0]));
  1.4151 +    std::exit(0);
  1.4152 +  }
  1.4153 +
  1.4154 +  if (cimg_option("-h",false,0) || cimg_option("-help",false,0) || cimg_option("--help",false,0)) {
  1.4155 +    cimg_usage("GREYC's Magic Image Converter");
  1.4156 +
  1.4157 +    char version[1024] = { 0 };
  1.4158 +    std::sprintf(version,"        Version %d.%d.%d.%d, Copyright (C) 2008-2009, David Tschumperle (http://gmic.sourceforge.net)",
  1.4159 +                 gmic_version/1000,(gmic_version/100)%10,(gmic_version/10)%10,gmic_version%10);
  1.4160 +    cimg_help(version);
  1.4161 +
  1.4162 +    cimg_help("\n  Usage\n"
  1.4163 +              "  -----");
  1.4164 +    cimg_help("  gmic [file_1] [file_2] .. [file_n] [command_1] .. [command_n] [file_n+1] ...\n");
  1.4165 +    cimg_help("  G'MIC is an interpreter of image processing macros whose goal is to convert, manipulate and");
  1.4166 +    cimg_help("  visualize generic 1D/2D/3D multi-spectral image and video files. It follows these simple rules :\n");
  1.4167 +    cimg_help("    - G'MIC handles a numbered list of images which are all stored in computer memory.");
  1.4168 +    cimg_help("    - The first image of the list has indice '[0]'.");
  1.4169 +    cimg_help("    - Negative indices are treated in a cyclic way (i.e. '[-1]' is the last image,");
  1.4170 +    cimg_help("      '[-2]' the penultimate one, and so on...).");
  1.4171 +    cimg_help("    - Command line items tell how to add/remove/manipulate/display images of the list.");
  1.4172 +    cimg_help("    - Items are read and executed in the order they appear on the command line, from the left to the right.");
  1.4173 +    cimg_help("    - Items can thus appear more than one time on the command line.");
  1.4174 +    cimg_help("    - An item starting by '-' is a G'MIC instruction.");
  1.4175 +    cimg_help("    - One instruction may have two equivalent names (regular and short).");
  1.4176 +    cimg_help("    - A G'MIC instruction may have mandatory or optional arguments.");
  1.4177 +    cimg_help("    - When multiple arguments are needed, they are separated by commas ','.");
  1.4178 +    cimg_help("    - Items that are not instructions are considered either as input filenames or input strings.");
  1.4179 +    cimg_help("    - When an input filename is encountered, the corresponding image data are loaded");
  1.4180 +    cimg_help("      and added to the end of the image list.");
  1.4181 +    cimg_help("      (see section 'Filename options' below for more informations on file input/output).");
  1.4182 +    cimg_help("    - Special filenames '-' or '-.ext' mean 'standard input/output' (optionally. in 'ext' format).");
  1.4183 +    cimg_help("    - Special input strings can be used to insert new images to the list. They can be :");
  1.4184 +    cimg_help("        - 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]' : Insert 'N' black images with specified size.");
  1.4185 +    cimg_help("          (adding '%' to a dimension means 'percentage of to the same dimension in the last image'),");
  1.4186 +    cimg_help("        - '[indice]' or '[indice]xN' : Insert 1 or N copies of the existing image [indice].");
  1.4187 +    cimg_help("        - '(v1,v2,...)' or '(v1,v2,...)xN' : Insert 1 or N copies of the specified IxJxKxL image.");
  1.4188 +    cimg_help("          Separators inside '(..)' can be ',' (column), ';' (line), '/' (slice) or '^' (channel).");
  1.4189 +    cimg_help("    - A G'MIC instruction may be restricted to a specific subset of the list, by adding '[subset]' to");
  1.4190 +    cimg_help("      the instruction name. Several usual expressions are possible for 'subset', for instance : ");
  1.4191 +    cimg_help("        '-command[0,1,3]' : Apply instruction on images 0,1 and 3.");
  1.4192 +    cimg_help("        '-command[3-5]' : Apply instruction on images 3 to 5.");
  1.4193 +    cimg_help("        '-command[50%-100%] : Apply instruction on the second half of the image list.");
  1.4194 +    cimg_help("        '-command[0,-2,-1]' : Apply instruction on the first and two latest images.");
  1.4195 +    cimg_help("        '-command[0-9:3]' : Apply instruction on images 0 to 9, with a step of 3 (i.e. images 0,3,6,9).");
  1.4196 +    cimg_help("        '-command[0,2-4,50%--1]' : Apply instruction on images 0,2,3,4 and the second half of the list.");
  1.4197 +    cimg_help("    - When no image subset is specified, a G'MIC instruction is applied on all images of the list.");
  1.4198 +    cimg_help("    - Native (non-macro) instructions starting with '--' instead of '-' do not act 'in-place' but");
  1.4199 +    cimg_help("      insert their result as a new image at the end of the list.");
  1.4200 +    cimg_help("    - On the command line, any item of the form '@indice' or '@{indice}' is replaced");
  1.4201 +    cimg_help("      by the values of the image '[indice]' separated by commas.");
  1.4202 +    cimg_help("    - Items '@?' (or '@{?}'), '@{?,max}' or '@{?,min,max}' are replaced by a float random value between [0,1], [0,max] or [min,max].");
  1.4203 +    cimg_help("    - Items '@[?]', '@[?,max]' or '@[?,min,max]' do the same but return integer random values.");
  1.4204 +    cimg_help("    - Restrictions to a subset of image values can be specified with '@{indice,subset}' (as in '@{2,0-50%}').");
  1.4205 +    cimg_help("    - Restriction to a particular pixel coordinate can be specified with '@(indice,x[,y[,z[,v[,borders]]]])'.");
  1.4206 +    cimg_help("    - On the command line, the item '@#' is replaced by the number of images in the list.");
  1.4207 +    cimg_help("    - Input filenames or commands may result to the generation of 3D objects.");
  1.4208 +    cimg_help("    - A 3D object viewer, based on software rendering, is included in G'MIC.");
  1.4209 +    cimg_help("    - A 3D object is stored as a single-column image containing all object data, in the following order :");
  1.4210 +    cimg_help("        { magic header, vertices, faces, colors, opacities }.");
  1.4211 +    cimg_help("    - Custom user-defined G'MIC instructions can be defined with the use of a macro file.");
  1.4212 +    cimg_help("    - A macro file is a simple ASCII text file, each line being of the form");
  1.4213 +    cimg_help("        'instruction_name : substitution' or 'substitution (continuation)' or '# comment'.");
  1.4214 +    cimg_help("    - Each invoked macro instruction is substituted as its defined content on the command line.");
  1.4215 +    cimg_help("    - A macro file 'gmic_def.raw' is distributed within the G'MIC package.");
  1.4216 +    cimg_help("    - The macros defined in 'gmic_def.raw' are included by default in G'MIC.");
  1.4217 +    cimg_help("    - Macros arguments, separated by commas, can be added after the invokation of a macro instruction.");
  1.4218 +    cimg_help("    - In macros definitions, expressions starting with '$' are used to reference macro arguments :");
  1.4219 +    cimg_help("        $i and ${i} are replaced by the value of the i-th macro argument.");
  1.4220 +    cimg_help("        $# and ${#} are replaced by the number of macro arguments.");
  1.4221 +    cimg_help("        $* and ${*} are replaced by the entire string of macro arguments.");
  1.4222 +    cimg_help("        ${i*} is replaced by all macro arguments following the i-th argument (included).");
  1.4223 +    cimg_help("        ${i=$#} is replaced by $i if defined, or by its new value $# else.");
  1.4224 +    cimg_help("        ${i=$j} is replaced by $i if defined, or by its new value $j else.");
  1.4225 +    cimg_help("        ${i=default} is replaced by $i if defined, or by its new value 'default' else.");
  1.4226 +    cimg_help("\n  A list of available native and macro instructions is available below.");
  1.4227 +    cimg_help("  A parameter specified in '[]' is optional, except when standing for '[indices]' where it");
  1.4228 +    cimg_help("  corresponds to one or several indices of the image list, as described above. In this case, the '[' and ']'");
  1.4229 +    cimg_help("  characters must explicitly appear when writting the item.");
  1.4230 +
  1.4231 +    cimg_help("\n  Global options\n"
  1.4232 +              "  --------------");
  1.4233 +    cimg_option("-help","(no args)","Display this help (eq. to '-h').");
  1.4234 +    cimg_option("-verbose+","(no args)","Increment verbosity level (eq. to '-v+').");
  1.4235 +    cimg_option("-verbose-","(no args)","Decrement verbosity level (eq. to '-v-').");
  1.4236 +    cimg_option("-macros","'filename'","Load macro file from specified filename (eq. to '-m').");
  1.4237 +    cimg_option("-debug","(no args)","Switch debug flag (when on, displays internal infos for debugging).");
  1.4238 +    cimg_option("-fullpath","(no args)","Switch full path flag (when on, displays full filename paths).");
  1.4239 +
  1.4240 +    cimg_help("\n  Arithmetic operators\n"
  1.4241 +              "  --------------------");
  1.4242 +    cimg_option("-add","'value', '[indice]' or (no args)","Add 'value' or '[indice]' to image(s)");
  1.4243 +    cimg_help("                                              "
  1.4244 +              "or add image(s) together (eq. to '-+').");
  1.4245 +    cimg_option("-sub","'value', '[indice]' or (no args)","Substract 'value' or '[indice]' to image(s)");
  1.4246 +    cimg_help("                                              "
  1.4247 +              "or substract image(s) together (eq. to '--').");
  1.4248 +    cimg_option("-mul","'value', '[indice]' or (no args)","Multiply image(s) by 'value' or '[indice]'");
  1.4249 +    cimg_help("                                              "
  1.4250 +              "or multiply image(s) together (eq. to '-*').");
  1.4251 +    cimg_option("-div","'value', '[indice]' or (no args)","Divide image(s) by 'value' or '[indice]'");
  1.4252 +    cimg_help("                                              "
  1.4253 +              "or divide image(s) together (eq. to '-/').");
  1.4254 +    cimg_option("-pow","'value', '[indice]' or (no args)","Compute image(s) to the power of 'value' or '[indice]'");
  1.4255 +    cimg_help("                                              "
  1.4256 +              "or power of the image(s) together (eq. to '-^').");
  1.4257 +    cimg_option("-min","'value', '[indice]' or (no args)","Compute minimum between image(s) and 'value' or '[indice]'");
  1.4258 +    cimg_help("                                              "
  1.4259 +              "or minimum of image(s) together.");
  1.4260 +    cimg_option("-max","'value', '[indice]' or (no args)","Compute maximum between image(s) and 'value' or '[indice]'");
  1.4261 +    cimg_help("                                              "
  1.4262 +              "or maximum of image(s) together.");
  1.4263 +    cimg_option("-mod","'value', '[indice]' or (no args)","Compute modulo of image(s) with 'value' or '[indice]'");
  1.4264 +    cimg_help("                                              "
  1.4265 +              "or modulo with image(s) together.");
  1.4266 +    cimg_option("-and","'value', '[indice]' or (no args)","Compute bitwise AND of image(s) with 'value' or '[indice]'");
  1.4267 +    cimg_help("                                              "
  1.4268 +              "or bitwise AND of image(s) together.");
  1.4269 +    cimg_option("-or","'value', '[indice]' or (no args)","Compute bitwise OR of image(s) with 'value' or '[indice]'");
  1.4270 +    cimg_help("                                              "
  1.4271 +              "or bitwise OR of image(s) together.");
  1.4272 +    cimg_option("-xor","'value', '[indice]' or (no args)","Compute bitwise XOR of image(s) with 'value' '[indice]'");
  1.4273 +    cimg_help("                                              "
  1.4274 +              "or bitwise XOR of image(s) together.");
  1.4275 +    cimg_option("-cos","(no args)","Compute cosine of image(s) values.");
  1.4276 +    cimg_option("-sin","(no args)","Compute sine of image(s) values.");
  1.4277 +    cimg_option("-tan","(no args)","Compute tangent of image(s) values.");
  1.4278 +    cimg_option("-acos","(no args)","Compute arccosine of image(s) values.");
  1.4279 +    cimg_option("-asin","(no args)","Compute arcsine of image(s) values.");
  1.4280 +    cimg_option("-atan","(no args)","Compute arctangent of image(s) values.");
  1.4281 +    cimg_option("-abs","(no args)","Compute absolute value of image(s) values.");
  1.4282 +    cimg_option("-sqr","(no args)","Compute square of image(s) values.");
  1.4283 +    cimg_option("-sqrt","(no args)","Compute square root of image(s) values.");
  1.4284 +    cimg_option("-exp","(no args)","Compute exponential of image(s) values.");
  1.4285 +    cimg_option("-log","(no args)","Compute logarithm of image(s) values.");
  1.4286 +    cimg_option("-log10","(no args)","Compute logarithm_10 of image(s) values.");
  1.4287 +
  1.4288 +    cimg_help("\n  Pointwise pixel manipulation\n"
  1.4289 +              "  ----------------------------");
  1.4290 +    cimg_option("-type","'value_type'","Cast all images into specified value type (eq. to '-t').");
  1.4291 +    cimg_help("                                              "
  1.4292 +              "('value_type' can be 'bool','uchar','char','ushort','short',");
  1.4293 +    cimg_help("                                              "
  1.4294 +              "'uint','int','float','double').");
  1.4295 +    cimg_option("-set","'value[,x[,y[,z[,v]]]]'","Set scalar value in image(s) at specified position (eq. to '-=').");
  1.4296 +    cimg_option("-endian","(no args)","Invert endianness of the image(s) buffers.");
  1.4297 +    cimg_option("-fill","'value1,value2,...'","Fill image(s) with scalar values in a repetitive way (eq. to '-f').");
  1.4298 +    cimg_option("-threshold","'value[%][,soft]' or (noargs)","Threshold pixel values ((noargs) for interactive mode).");
  1.4299 +    cimg_option("-cut","'{value1[%],[indice]},{value2[%],[indice]}' or (noargs)","Cut pixel values in specified range ");
  1.4300 +    cimg_help("                                              "
  1.4301 +              "((noargs) for interactive mode).");
  1.4302 +    cimg_option("-normalize","'{value1[%],[indice]},{value2[%],[indice]}'",
  1.4303 +                "Normalize pixel values in specified range (eq. to '-n').");
  1.4304 +    cimg_option("-round","'round_value[,round_type]'","Round pixel values.");
  1.4305 +    cimg_option("-equalize","'nb_levels'","Equalize image(s) histogram(s) using specified number of levels.");
  1.4306 +    cimg_option("-quantize","'nb_levels'","Quantize image(s) with 'nb_levels' levels.");
  1.4307 +    cimg_option("-noise","'std[%][,noise_type]'","Add noise with specified standard deviation");
  1.4308 +    cimg_help("                                              "
  1.4309 +              "('noise_type' can be '{0=gaussian, 1=uniform, 2=salt&pepper, 3=poisson}'.");
  1.4310 +    cimg_option("-norm","(no args)","Compute pointwise L2-norm of vector-valued image(s).");
  1.4311 +    cimg_option("-orientation","(no args)","Compute pointwise orientation of vector-valued image(s).");
  1.4312 +
  1.4313 +    cimg_help("\n  Color bases conversions\n"
  1.4314 +              "  -----------------------");
  1.4315 +    cimg_option("-rgb2hsv","(no args)","Convert image(s) from RGB to HSV colorbases.");
  1.4316 +    cimg_option("-rgb2hsl","(no args)","Convert image(s) from RGB to HSL colorbases.");
  1.4317 +    cimg_option("-rgb2hsi","(no args)","Convert image(s) from RGB to HSI colorbases.");
  1.4318 +    cimg_option("-rgb2yuv","(no args)","Convert image(s) from RGB to YUV colorbases.");
  1.4319 +    cimg_option("-rgb2ycbcr","(no args)","Convert image(s) from RGB to YCbCr colorbases.");
  1.4320 +    cimg_option("-rgb2xyz","(no args)","Convert image(s) from RGB to XYZ colorbases.");
  1.4321 +    cimg_option("-rgb2lab","(no args)","Convert image(s) from RGB to Lab colorbases.");
  1.4322 +    cimg_option("-rgb2cmy","(no args)","Convert image(s) from RGB to CMY colorbases.");
  1.4323 +    cimg_option("-rgb2cmyk","(no args)","Convert image(s) from RGB to CMYK colorbases.");
  1.4324 +    cimg_option("-rgb2lut","'[indice][,dithering]' or 'LUT_type[,dithering]'","Index image(s) with color palette ");
  1.4325 +    cimg_help("                                              "
  1.4326 +              "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'");
  1.4327 +    cimg_help("                                              "
  1.4328 +              "'dithering' can be '{0=off, 1=on}').");
  1.4329 +    cimg_option("-hsv2rgb","(no args)","Convert image(s) from HSV to RGB colorbases.");
  1.4330 +    cimg_option("-hsl2rgb","(no args)","Convert image(s) from HSL to RGB colorbases.");
  1.4331 +    cimg_option("-hsi2rgb","(no args)","Convert image(s) from HSI to RGB colorbases.");
  1.4332 +    cimg_option("-yuv2rgb","(no args)","Convert image(s) from YUV to RGB colorbases.");
  1.4333 +    cimg_option("-ycbcr2rgb","(no args)","Convert image(s) from YCbCr to RGB colorbases.");
  1.4334 +    cimg_option("-xyz2rgb","(no args)","Convert image(s) from XYZ to RGB colorbases.");
  1.4335 +    cimg_option("-lab2rgb","(no args)","Convert image(s) from Lab to RGB colorbases.");
  1.4336 +    cimg_option("-cmy2rgb","(no args)","Convert image(s) from CMY to RGB colorbases.");
  1.4337 +    cimg_option("-cmyk2rgb","(no args)","Convert image(s) from CMYK to RGB colorbases.");
  1.4338 +    cimg_option("-lut2rgb","'[indice]' or 'LUT_type'","Map color palette to image(s) ");
  1.4339 +    cimg_help("                                              "
  1.4340 +              "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'.");
  1.4341 +
  1.4342 +    cimg_help("\n  Geometric manipulation\n"
  1.4343 +              "  ----------------------");
  1.4344 +    cimg_option("-resize","'[indice][,interpolation[,borders[,center]]]' or ","");
  1.4345 +    cimg_help("                     "
  1.4346 +              "'{[indice],width[%]}[x{[indice],height[%]}[x{[indice],depth[%]}[x{[indice],dim[%]}[,interpolation[,borders[,center]]]]]]'");
  1.4347 +    cimg_help("                                              "
  1.4348 +              "or (noargs)");
  1.4349 +    cimg_help("                                              "
  1.4350 +              "Resize image(s) to specified geometry ((noargs) for interactive mode) (eq. to '-r')");
  1.4351 +    cimg_help("                                              "
  1.4352 +              "('interpolation' can be '{0=none, 1=nearest, 2=average, 3=linear, 4=grid, 5=cubic}').");
  1.4353 +    cimg_option("-resize2x","(no args)","Resize image(s) with Scale2x.");
  1.4354 +    cimg_option("-resize3x","(no args)","Resize image(s) with Scale3x.");
  1.4355 +    cimg_option("-crop","'x0[%],x1[%][,border_conditions]' or 'x0[%],y0[%],x1[%],y1[%][,border_conditions]' or ","");
  1.4356 +    cimg_help("                                              "
  1.4357 +              "'x0[%],y0[%],z0[%],x1[%],y1[%],z1[%][,border_conditions]' or ");
  1.4358 +    cimg_help("                                              "
  1.4359 +              "'x0[%],y0[%],z0[%],v0[%],x1[%],y1[%],z1[%],v1[%][,border_conditions]' or (noargs).");
  1.4360 +    cimg_help("                                              "
  1.4361 +              "Crop image(s) using specified geometry ((noargs) for interactive mode) (eq. to '-c') ");
  1.4362 +    cimg_help("                                              "
  1.4363 +              "('border_conditions' can be '{0=zero, 1=nearest}')");
  1.4364 +    cimg_help("                                              "
  1.4365 +              "((no args) for interactive mode).");
  1.4366 +    cimg_option("-autocrop","'value1,value2,...'","Autocrop image(s) using specified background color.");
  1.4367 +    cimg_option("-channels","'{[ind0],v0[%]}[,{[ind1],v1[%]}]'","Select channels v0..v1 of multi-spectral image(s).");
  1.4368 +    cimg_option("-slices","'{[ind0],z0[%]}[,{[ind1],z1[%]}]'","Select slices z0..z1 of volumetric image(s).");
  1.4369 +    cimg_option("-lines","'{[ind0],y0[%]}[,{[ind1],y1[%]}]'","Select lines y0..y1 of image(s).");
  1.4370 +    cimg_option("-columns","'{[ind0],x0[%]}[,{[ind1],x1[%]}]'","Select columns x0..x1 of image(s).");
  1.4371 +    cimg_option("-rotate","'angle[,border_conditions]'","Rotate image(s) with a given angle ");
  1.4372 +    cimg_help("                                              "
  1.4373 +              "('border_conditions' can be '{-3=cyclic (in-place), -2=nearest(ip), -1=zero(ip), 0=zero, 1=nearest, 2=cyclic}'");
  1.4374 +    cimg_help("                                              "
  1.4375 +              "and 'interpolation' can be '{0=none, 1=linear, 2=cubic}').");
  1.4376 +    cimg_option("-mirror","'axis'",
  1.4377 +                "Mirror image(s) along specified axis ('axis' can be '{x,y,z,v}').");
  1.4378 +    cimg_option("-translate","'tx[%][,ty[%][,tz[%][,tv[%][,border_conditions]]]]'",
  1.4379 +                "Translate image(s) by vector (dx,dy,dz,dv)");
  1.4380 +    cimg_help("                                              "
  1.4381 +              "('border_conditions' can be '{0=zero, 1=nearest, 2=cyclic}').");
  1.4382 +    cimg_option("-transpose","(no args)","Transpose image(s).");
  1.4383 +    cimg_option("-invert","(no args)","Compute inverse matrix.");
  1.4384 +    cimg_option("-permute","'permutation'","Permute image axes by the specified permutation "
  1.4385 +                "('permutation' can be 'yxzv',...).");
  1.4386 +    cimg_option("-unroll","'axis'",
  1.4387 +                "Unroll image(s) along specified axis ('axis' can be '{x,y,z,v}').");
  1.4388 +    cimg_option("-split","'axis[,nb_parts]' or 'value[,keep]'",
  1.4389 +                "Split image(s) along specified axis or value ('axis' can be '{x,y,z,v}') (eq. to '-s').");
  1.4390 +    cimg_option("-append","'axis,[alignement]'","Append image(s) along specified axis and alignement (eq. to '-a')");
  1.4391 +    cimg_help("                                              "
  1.4392 +              "('axis' can be '{x,y,z,v}' and 'alignement' can be '{p=left, c=center, n=right)'.");
  1.4393 +    cimg_option("-warp","'[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]'",
  1.4394 +                "Warp image(s) in 'nb_frames' with field '[indice]' ");
  1.4395 +    cimg_help("                                              "
  1.4396 +              "('relative' can be '{0,1}', 'interpolation' can be '{0,1}', "
  1.4397 +              "'border_conditions' can be '{0=zero, 1=nearest}').");
  1.4398 +
  1.4399 +    cimg_help("\n  Image filtering\n"
  1.4400 +              "  ---------------");
  1.4401 +    cimg_option("-blur","'std[,border_conditions]'",
  1.4402 +                "Apply gaussian blur of specified standard deviation");
  1.4403 +    cimg_help("                                              "
  1.4404 +              "('border_conditions' can be '{0=zero, 1=nearest}').");
  1.4405 +    cimg_option("-bilateral","'stdevs,stdevr'",
  1.4406 +                "Apply bilateral filtering with specified standard deviations 'stdevs' and 'stdevr'.");
  1.4407 +    cimg_option("-smooth","'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]'","");
  1.4408 +    cimg_help("                                              "
  1.4409 +              "Smooth image(s) anisotropically with specified parameters.");
  1.4410 +    cimg_option("-denoise","'patch_size[,stdev_p[,stdev_s[,lookup_size]]]'",
  1.4411 +                "Denoise image(s) with a patch-averaging procedure.");
  1.4412 +    cimg_option("-median","'size'","Apply median filter with specified size.");
  1.4413 +    cimg_option("-sharpen","'amplitude[,0]' or 'amplitude,1[,edge[,alpha[,sigma]]]'",
  1.4414 +                "Sharpen image(s) with inverse diffusion or shock filters.");
  1.4415 +    cimg_option("-convolve","'[indice][,border_conditions]'",
  1.4416 +                "Convolve image(s) by the specified mask");
  1.4417 +    cimg_help("                                              "
  1.4418 +              "('border_conditions' can be '{0=zero, 1=nearest}').");
  1.4419 +    cimg_option("-correlate","'[indice][,border_conditions]'",
  1.4420 +                "Correlate image(s) by the specified mask (same parameters as above).");
  1.4421 +    cimg_option("-erode","'size[,border_conditions]' or '[indice][,border_conditions]'","");
  1.4422 +    cimg_help("                                              "
  1.4423 +              "Erode image(s) by the specified mask (same parameters as above)').");
  1.4424 +    cimg_option("-dilate","'size[,border_conditions]' or '[indice][,border_conditions]'","");
  1.4425 +    cimg_help("                                              "
  1.4426 +              "Dilate image(s) by the specified mask (same parameters as above).");
  1.4427 +    cimg_option("-gradient","'x', 'xy', 'xyz' or (no args)","Compute image gradient.");
  1.4428 +    cimg_option("-hessian","'{xx,xy,xz,yy,yz,zz}' or (no args)","Compute image Hessian.");
  1.4429 +    cimg_option("-fft","(no args)","Compute direct Fourier transform.");
  1.4430 +    cimg_option("-ifft","(no args)","Compute inverse Fourier transform.");
  1.4431 +
  1.4432 +    cimg_help("\n  Image creation and drawing\n"
  1.4433 +              "  --------------------------");
  1.4434 +    cimg_option("-dimensions","(no args)","Get dimensions of the image(s) as a 1x4 vector.");
  1.4435 +    cimg_option("-stats","(no args)","Get statistics of the image(s) as a 1x6 vector.");
  1.4436 +    cimg_option("-histogram","'nb_values[%]'","Compute histogram of image(s) with 'nb_values' values.");
  1.4437 +    cimg_option("-distance","'isovalue'","Compute distance function(s) to specified isovalue.");
  1.4438 +    cimg_option("-hamilton","'nb_iter[,band_size]'","Apply Hamilton-Jacobi PDE to compute distance to 0.");
  1.4439 +    cimg_option("-label","(no args)","Label connected components of image(s).");
  1.4440 +    cimg_option("-displacement","'[indice][,smoothness[,precision[,nbscales[,itermax]]]]","Estimate smooth displacement field between image(s) "
  1.4441 +                "and specified target '[indice]'.");
  1.4442 +    cimg_option("-sort","(no args)","Sort values of image(s) in increasing order.");
  1.4443 +    cimg_option("-psnr","'max_value' or (noargs)","Compute PSNR between specified image(s).");
  1.4444 +    cimg_option("-point","'x[%],y[%][,z[%][,opacity[,color]]]'","Draw 3D colored point on specified image(s).");
  1.4445 +    cimg_option("-line","'x0[%],y0[%],x1[%],y1[%][,opacity[,color]]'","Draw 2D colored line on specified image(s).");
  1.4446 +    cimg_option("-polygon","'N,x0[%],y0[%],..,xN[%],yN[%][,opacity[,color]]'","Draw a 2D colored N-vertices polygon on specified image(s).");
  1.4447 +    cimg_option("-ellipse","'x[%],y[%],r,R[,u,v[,opacity[,color]]]'","Draw 2D colored ellipse on specified image(s).");
  1.4448 +    cimg_option("-text","text,x[%],y[%],size[,opacity[,color]]'",
  1.4449 +                "Draw specified text at position (x,y) and with specified font size.");
  1.4450 +    cimg_option("-image","'[indice][,x[%][,y[%][,z[%][,opacity[,ind_mask]]]]]'","Draw sprite image on specified image(s).");
  1.4451 +    cimg_option("-object3d","'[indice][,x[%][,y[%][,z[,opacity]]]]'","Draw 3D object on specified image(s).");
  1.4452 +    cimg_option("-plasma","'alpha[,beta[,opacity]]'","Draw plasma on specified image(s).");
  1.4453 +    cimg_option("-mandelbrot","'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]'","Draw Mandelbrot/Julia fractals on specified image(s).");
  1.4454 +    cimg_option("-flood","'x[%][,y[%][,z[%][,tolerance[,opacity[,color]]]]]'",
  1.4455 +                "Flood-fill image(s) starting from (x,y,z) with specified tolerance.");
  1.4456 +
  1.4457 +    cimg_help("\n  List manipulation\n"
  1.4458 +              "  -----------------");
  1.4459 +    cimg_option("-remove","(no args)","Remove image(s) from list (eq. to '-rm').");
  1.4460 +    cimg_option("-keep","(no args)","Keep only specified image(s) (eq. to '-k').");
  1.4461 +    cimg_option("-move","'position'","Move image(s) at specified position (eq. to '-mv').");
  1.4462 +    cimg_option("-reverse","(no args)","Reverse image(s) order.");
  1.4463 +    cimg_option("-name","\"name\"","Set display name of image(s).");
  1.4464 +
  1.4465 +    cimg_help("\n  3D Rendering\n"
  1.4466 +              "  ------------");
  1.4467 +    cimg_option("-cube3d","'size'","Insert a 3D cube at the end of the list.");
  1.4468 +    cimg_option("-cone3d","'radius[,height[,subdivisions]]'","Insert a 3D cube at the end of the list.");
  1.4469 +    cimg_option("-cylinder3d","'radius[,height[,subdivisions]]'","Insert a 3D cylinder at the end of the list.");
  1.4470 +    cimg_option("-torus3d","'radius1,radius2[,subdivisions1,subdivisions2]'","Insert a 3D torus at the end of the list.");
  1.4471 +    cimg_option("-plane3d","'sizex,sizey[,subdivisionsx,subdisivionsy]'","Insert a 3D plane at the end of the list.");
  1.4472 +    cimg_option("-sphere3d","'radius[,subdivisions]'","Insert a 3D sphere at the end of the list.");
  1.4473 +    cimg_option("-elevation3d","'z-factor' or '[indice]'",
  1.4474 +                "Generate 3D elevation(s) of image(s) using specified z-factor or elevation map.");
  1.4475 +    cimg_option("-isovalue3d","'value'","Generate 3D object(s) by retrieving isophote or isosurface of image(s).");
  1.4476 +    cimg_option("-center3d","(no args)","Center 3D object(s) (eq. to '-c3d').");
  1.4477 +    cimg_option("-normalize3d","(no args)","Normalize 3D object(s) to a unit size (eq. to '-n3d').");
  1.4478 +    cimg_option("-rotate3d","'u,v,w,angle'","Rotate 3D object(s) around axis (u,v,w) with specified angle (eq. to '-rot3d').");
  1.4479 +    cimg_option("-add3d","'[indice]' or 'tx,ty,tz' or (noargs)","Append or translate 3D object(s), or append 3D object(s) together (eq. to '-+3d').");
  1.4480 +    cimg_option("-sub3d","'tx,ty,tz'","Translate 3D object(s) with the opposite of the specified vector (eq. to '--3d').");
  1.4481 +    cimg_option("-mul3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified factor (eq. to '-*3d').");
  1.4482 +    cimg_option("-div3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified inverse factor (eq. to '-/3d').");
  1.4483 +    cimg_option("-color3d","'R,G,B[,opacity]'","Set color of 3D object(s) (eq. to '-col3d').");
  1.4484 +    cimg_option("-opacity3d","'opacity'","Set opacity of 3D object(s) (eq. to '-opac3d').");
  1.4485 +    cimg_option("-invert3d","(no args)","Invert primitive orientations of 3D object(s) (eq. to '-i3d').");
  1.4486 +    cimg_option("-split3d","(no args)","Split 3D object data into 6 data vectors 'header,N,vertices,primitives,colors,opacities' (eq. to '-s3d').");
  1.4487 +    cimg_option("-light3d","'posx,posy,posz'","Set the 3D position of the light for 3D rendering (eq. to '-l3d').");
  1.4488 +    cimg_option("-focale3d","'value'","Set focale value for 3D rendering (eq. to '-f3d').");
  1.4489 +    cimg_option("-specl3d","'value'","Set amount of specular light for 3D rendering (eq. to '-sl3d').");
  1.4490 +    cimg_option("-specs3d","'value'","Set shininess of specular light for 3D rendering (eq. to '-ss3d').");
  1.4491 +    cimg_option("-orient3d","(no args)","Switch double-sided mode for 3D rendering (eq. to '-o3d').");
  1.4492 +    cimg_option("-render3d","'mode'","Set 3D rendering mode");
  1.4493 +    cimg_help("                                              "
  1.4494 +              "(can be '{-1=bounding-box, 0=pointwise, 1=linear, 2=flat, 3=flat-shaded, 4=Gouraud-shaded, 5=Phong-shaded}') (eq. to '-r3d').");
  1.4495 +    cimg_option("-renderd3d","'mode'","Set dynamic rendering mode in 3D viewer (same values as above) (eq. to '-rd3d').");
  1.4496 +    cimg_option("-background3d","'R,G,B'","Define background color in 3D viewer (eq. to '-b3d').");
  1.4497 +
  1.4498 +    cimg_help("\n  Program controls\n"
  1.4499 +              "  ----------------");
  1.4500 +    cimg_option("-nop","(no args)","Do nothing.");
  1.4501 +    cimg_option("-skip","(any args)","Do nothing but skip the next argument.");
  1.4502 +    cimg_option("-echo","'text'","Output specified message (eq. to '-e').");
  1.4503 +    cimg_option("-print","(no args)","Print image(s) informations (eq. to '-p').");
  1.4504 +    cimg_option("-quit","(no args)","Force interpreter to quit (eq. to '-q').");
  1.4505 +    cimg_option("-do","(no args)","Start a 'do-while' code bloc.");
  1.4506 +    cimg_option("-while","'cond'","End a 'do-while' code bloc and go back to associated '-do' if 'cond' is a strictly positive value.");
  1.4507 +    cimg_option("-if","'cond'","Start a 'if-then-else' code bloc and test if 'cond' is a strictly positive value.");
  1.4508 +    cimg_option("-else","(no args)","Execute following commands if previous '-if' condition failed.");
  1.4509 +    cimg_option("-endif","(no args)","End a 'if-then-else' code bloc");
  1.4510 +    cimg_option("-repeat","'N'","Start a 'repeat-done' code bloc.");
  1.4511 +    cimg_option("-done","(no args)","End a 'repeat-done' code bloc, and go to associated '-repeat' if iterations remain.");
  1.4512 +    cimg_option("-int","'arg1,...,argN'","Check if all specified arguments are integer. If not, print an error message and exit.");
  1.4513 +    cimg_option("-float","'arg1,...,argN","Check if all specified arguments are float values. If not, print an error message and exit.");
  1.4514 +
  1.4515 +    cimg_help("\n  Input/output\n"
  1.4516 +              "  ------------");
  1.4517 +    cimg_option("-input","'filename' or 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]'","");
  1.4518 +    cimg_help("                     "
  1.4519 +              "or '[indice][xN]' or '(v11{,;/^}v21...vLM)[xN]'");
  1.4520 +    cimg_help("                                              "
  1.4521 +              "Input filename, empty image, image copy, or image with specified values (eq. to '-i' or (no args)).");
  1.4522 +    cimg_option("-output","'filename'","Output image(s) in specified filename (eq. to '-o').");
  1.4523 +    cimg_option("-display","(no args)","Display image(s) (eq. to '-d').");
  1.4524 +    cimg_option("-display3d","(no args)","Display 3D object(s) (eq. to '-d3d').");
  1.4525 +    cimg_option("-plot","'[plot_type[,vertex_type[,xmin[,xmax[,ymin[,ymax]]]]]]'",
  1.4526 +                "Display image(s) as 1D plot(s)");
  1.4527 +    cimg_help("                                              "
  1.4528 +              "('plot_type' can be '{0=none, 1=lines, 2=splines, 3=bar}').");
  1.4529 +    cimg_help("                                              "
  1.4530 +              "('vertex_type' can be in '[0-7]').");
  1.4531 +    cimg_option("-select","'select_type'","Select feature from image(s) in an interactive way");
  1.4532 +    cimg_help("                                              "
  1.4533 +              "('select_type' can be in '{0=point, 1=line, 2=rectangle, 3=circle').");
  1.4534 +
  1.4535 +    // Print descriptions of default macros.
  1.4536 +    char line[256*1024] = { 0 }, name[4096] = { 0 }, args[4096] = { 0 }, desc[4096] = { 0 };
  1.4537 +    bool first_description = true;
  1.4538 +    for (const char *data = data_def; *data; ) {
  1.4539 +      if (*data=='\n') ++data;
  1.4540 +      else {
  1.4541 +        if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line);
  1.4542 +        if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
  1.4543 +          if (first_description) cimg_help("\n  Commands : Default macros\n"
  1.4544 +                                           "  -------------------------");
  1.4545 +          std::fprintf(cimg_stdout,"%s    %s-%-15s%s %-24s %s%s%s",
  1.4546 +                       first_description?"":"\n",
  1.4547 +                       cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
  1.4548 +          first_description = false;
  1.4549 +        }
  1.4550 +      }
  1.4551 +    }
  1.4552 +
  1.4553 +    // Print descriptions of use-defined macros.
  1.4554 +    first_description = true;
  1.4555 +    for (int i = 1; i<argc-1; ++i) if (!cimg::strcmp("-m",argv[i]) || !cimg::strcmp("-macros",argv[i])) {
  1.4556 +      std::FILE *file = cimg::fopen(argv[i+1],"r");
  1.4557 +      if (file) {
  1.4558 +        int err = 0;
  1.4559 +        while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
  1.4560 +          if (err) { // Non empty-line
  1.4561 +            name[0] = args[0] = desc[0] = 0;
  1.4562 +            if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
  1.4563 +              if (first_description) cimg_help("\n\n  Commands : User-defined macros\n"
  1.4564 +                                               "  ------------------------------");
  1.4565 +              std::fprintf(cimg_stdout,"%s    %s-%-15s%s %-24s %s%s%s",
  1.4566 +                           first_description?"":"\n",
  1.4567 +                           cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
  1.4568 +              first_description = false;
  1.4569 +            }
  1.4570 +          }
  1.4571 +        }
  1.4572 +      }
  1.4573 +      cimg::fclose(file);
  1.4574 +    }
  1.4575 +
  1.4576 +    cimg_help("\n\n  Viewers shortcuts\n"
  1.4577 +              "  -----------------");
  1.4578 +    cimg_help("  When displaying image(s) or 3D object(s) with G'MIC, you can use these shortcuts in viewers :");
  1.4579 +    cimg_help("   - CTRL+D : Increase window size.");
  1.4580 +    cimg_help("   - CTRL+C : Decrease window size.");
  1.4581 +    cimg_help("   - CTRL+R : Reset window size.");
  1.4582 +    cimg_help("   - CTRL+F : Toggle fullscreen mode.");
  1.4583 +    cimg_help("   - CTRL+S : Save current window snapshot.");
  1.4584 +    cimg_help("   - CTRL+O : Save current instance of viewed image or 3D object.\n");
  1.4585 +    cimg_help("  Specific options for the viewer of image(s) :");
  1.4586 +    cimg_help("   - CTRL+P             : Play stack of frames as a movie.");
  1.4587 +    cimg_help("   - CTRL+(mousewheel)  : Zoom in/out.");
  1.4588 +    cimg_help("   - SHIFT+(mousewheel) : Go left/right.");
  1.4589 +    cimg_help("   - ALT+(mousewheel)   : Go up/down.");
  1.4590 +    cimg_help("   - Numeric PAD        : Zoom in/out (+/-) and move zoomed region (numbers).");
  1.4591 +    cimg_help("   - BACKSPACE          : Reset zoom.\n");
  1.4592 +    cimg_help("  Specific options for the viewer of 3D object(s) :");
  1.4593 +    cimg_help("   - (mouse) + (left mouse button)   : Rotate object.");
  1.4594 +    cimg_help("   - (mouse) + (right mouse button)  : Zoom object.");
  1.4595 +    cimg_help("   - (mouse) + (middle mouse button) : Translate object.");
  1.4596 +    cimg_help("   - (mousewheel)                    : Zoom in/out.");
  1.4597 +    cimg_help("   - CTRL + Z : Enable/disable Z-buffer rendering");
  1.4598 +
  1.4599 +    cimg_help("\n  File options\n"
  1.4600 +              "  ------------");
  1.4601 +    cimg_help("  G'MIC is able to read/write most of the classical image file formats, including :");
  1.4602 +    cimg_help("   - 2D grayscale/color images : PNG,JPEG,GIF,PNM,TIFF,BMP,....");
  1.4603 +    cimg_help("   - 3D volumetric images : DICOM,HDR,NII,PAN,CIMG,INR,....");
  1.4604 +    cimg_help("   - Video files : MPEG,AVI,MOV,OGG,FLV,...");
  1.4605 +    cimg_help("   - Generic data files : DLM,ASC,RAW,TXT.");
  1.4606 +    cimg_help("   - 3D objects : OFF.\n");
  1.4607 +    cimg_help("  Specific options :");
  1.4608 +    cimg_help("   - For video files : you can read only sub-frames of the image sequence (recommended) with the expression");
  1.4609 +    cimg_help("     'video.ext,[first_frame[%][,last_frame[%][,step]]]'.");
  1.4610 +    cimg_help("   - For RAW files : you must specify the image dimensions with the expression");
  1.4611 +    cimg_help("     'file.raw,width[,height[,depth[,dim]]]]'.");
  1.4612 +    cimg_help("   - For YUV files : you must specify the image dimensions and can read only sub-frames of the image sequence with the expression");
  1.4613 +    cimg_help("     'file.yuv,width,height[,first_frame[,last_frame[,step]]]'.");
  1.4614 +    cimg_help("   - For JPEG files : you can specify the quality (in %) of an output jpeg file format with the expression");
  1.4615 +    cimg_help("     'file.jpg,30%'.");
  1.4616 +
  1.4617 +    cimg_help("\n  Examples of use\n"
  1.4618 +              "  ---------------");
  1.4619 +    cimg_help("  G'MIC is a simple but quite complete interpreter of image processing instructions, and can be used for a wide variety of");
  1.4620 +    cimg_help("  image processing tasks. Here are (very few) examples of how the command line tool G'MIC can be used :\n");
  1.4621 +    cimg_help("   - View image data : ");
  1.4622 +    cimg_help("     gmic file1.bmp file2.jpeg");
  1.4623 +    cimg_help("   - Convert image files : ");
  1.4624 +    cimg_help("     gmic input.bmp -o output.jpg");
  1.4625 +    cimg_help("   - Create volumetric image(s) from movie sequence : ");
  1.4626 +    cimg_help("     gmic input.mpg -a z -o output.hdr");
  1.4627 +    cimg_help("   - Compute image gradient norm : ");
  1.4628 +    cimg_help("     gmic input.bmp -gradient_norm");
  1.4629 +    cimg_help("   - Create G'MIC 3D logo : ");
  1.4630 +    cimg_help("     gmic 180x70x1x3 -text G\\'MIC,30,5,50,1,1 -blur 2 -n 0,100 [0] -plasma[1] \\");
  1.4631 +    cimg_help("     -+ -blur 1 -elevation -0.1 -rd3d 4");
  1.4632 +    cimg_help("\n  See also the macros defined in the provided macro file 'gmic_def.raw' for other examples.");
  1.4633 +
  1.4634 +    cimg_help("\n  ** G'MIC comes with ABSOLUTELY NO WARRANTY; "
  1.4635 +              "for details visit http://gmic.sourceforge.net **");
  1.4636 +    std::exit(0);
  1.4637 +  }
  1.4638 +
  1.4639 +  // Launch G'MIC instance.
  1.4640 +  //-----------------------
  1.4641 +  CImgList<float> images;
  1.4642 +  try {
  1.4643 +    gmic(argc,argv,images);
  1.4644 +  } catch (gmic_exception &e) {
  1.4645 +    std::fprintf(cimg_stdout,"\n** Error occurred : %s **\n",e.message);
  1.4646 +  }
  1.4647 +  return 0;
  1.4648 +}
  1.4649 +#endif
  1.4650 +
  1.4651 +#endif // #ifdef cimg_plugin ... #else ...