PTdecode/CImg-1.3.0/examples/gmic.cpp

Wed, 05 Aug 2009 17:32:05 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 17:32:05 +0100
changeset 18
fd1c6f6066da
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

updated README

     1 /*
     2  #
     3  #  File        : gmic.cpp
     4  #                ( C++ source file )
     5  #
     6  #  Description : GREYC's Magic Image Converter (library and executable)
     7  #                ( http://gmic.sourceforge.net )
     8  #                This file is a part of the CImg Library project.
     9  #                ( http://cimg.sourceforge.net )
    10  #
    11  #  Copyright   : David Tschumperle
    12  #                ( http://www.greyc.ensicaen.fr/~dtschump/ )
    13  #
    14  #  License     : CeCILL v2.0
    15  #                ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
    16  #
    17  #  This software is governed by the CeCILL  license under French law and
    18  #  abiding by the rules of distribution of free software.  You can  use,
    19  #  modify and/ or redistribute the software under the terms of the CeCILL
    20  #  license as circulated by CEA, CNRS and INRIA at the following URL
    21  #  "http://www.cecill.info".
    22  #
    23  #  As a counterpart to the access to the source code and  rights to copy,
    24  #  modify and redistribute granted by the license, users are provided only
    25  #  with a limited warranty  and the software's author,  the holder of the
    26  #  economic rights,  and the successive licensors  have only  limited
    27  #  liability.
    28  #
    29  #  In this respect, the user's attention is drawn to the risks associated
    30  #  with loading,  using,  modifying and/or developing or reproducing the
    31  #  software by the user in light of its specific status of free software,
    32  #  that may mean  that it is complicated to manipulate,  and  that  also
    33  #  therefore means  that it is reserved for developers  and  experienced
    34  #  professionals having in-depth computer knowledge. Users are therefore
    35  #  encouraged to load and test the software's suitability as regards their
    36  #  requirements in conditions enabling the security of their systems and/or
    37  #  data to be ensured and,  more generally, to use and operate it in the
    38  #  same conditions as regards security.
    39  #
    40  #  The fact that you are presently reading this means that you have had
    41  #  knowledge of the CeCILL license and that you accept its terms.
    42  #
    43 */
    45 // Add specific G'MIC methods to the CImg<T> class.
    46 //-------------------------------------------------
    47 #ifdef cimg_plugin
    49 template<typename t>
    50 CImg<T> get_replace(const CImg<t>& img) const {
    51   return +img;
    52 }
    54 template<typename t>
    55 CImg<T>& replace(CImg<t>& img) {
    56   return img.transfer_to(*this);
    57 }
    59 CImg<T>& gmic_set(const double value, const int x, const int y, const int z, const int v) {
    60   (*this).atXYZV(x,y,z,v,0) = (T)value;
    61   return *this;
    62 }
    64 CImg<T> get_gmic_set(const double value, const int x, const int y, const int z, const int v) const {
    65   return (+*this).gmic_set(value,x,y,z,v);
    66 }
    68 CImg<T> get_draw_point(const int x, const int y, const int z,
    69                        const CImg<T>& col, const float opacity) const {
    70   return (+*this).draw_point(x,y,z,col,opacity);
    71 }
    73 CImg<T> get_draw_line(const int x0, const int y0, const int x1, const int y1,
    74                       const CImg<T>& col, const float opacity) const {
    75   return (+*this).draw_line(x0,y0,x1,y1,col,opacity);
    76 }
    78 template<typename t>
    79 CImg<T> get_draw_polygon(const CImg<t>& pts, const CImg<T>& col, const float opacity) const {
    80   return (+*this).draw_polygon(pts,col,opacity);
    81 }
    83 CImg<T> get_draw_ellipse(const int x, const int y, const float r0, const float r1,
    84                          const float ru, const float rv, const CImg<T>& col, const float opacity) const {
    85   return (+*this).draw_ellipse(x,y,r0,r1,ru,rv,col,opacity);
    86 }
    88 CImg<T> get_draw_text(const int x, const int y, const char *const text, const T *const col,
    89                       const int bg, const float opacity,const int siz) const {
    90   return (+*this).draw_text(x,y,text,col,bg,opacity,siz);
    91 }
    93 CImg<T> get_draw_image(const int x, const int y, const int z,
    94                        const CImg<T>& sprite, const CImg<T>& mask, const float opacity) const {
    95   return (+*this).draw_image(x,y,z,sprite,mask,opacity);
    96 }
    98 CImg<T> get_draw_image(const int x, const int y, const int z,
    99                        const CImg<T>& sprite, const float opacity) const {
   100   return (+*this).draw_image(x,y,z,sprite,opacity);
   101 }
   103 CImg<T> get_draw_plasma(const float alpha, const float beta, const float opacity) const {
   104   return (+*this).draw_plasma(alpha,beta,opacity);
   105 }
   107 CImg<T> get_draw_mandelbrot(const CImg<T>& color_palette, const float opacity,
   108                             const double z0r, const double z0i, const double z1r, const double z1i,
   109                             const unsigned int itermax, const bool normalized_iteration,
   110                             const bool julia_set, const double paramr, const double parami) const {
   111   return (+*this).draw_mandelbrot(color_palette,opacity,z0r,z0i,z1r,z1i,itermax,
   112                                   normalized_iteration,julia_set,paramr,parami);
   113 }
   115 CImg<T> get_draw_fill(const int x, const int y, const int z,
   116                       const CImg<T>& col, const float opacity, const float tolerance) const {
   117   return (+*this).draw_fill(x,y,z,col,opacity,tolerance);
   118 }
   120 bool is_CImg3d() const {
   121   const bool is_header = (width==1 && height>=8 && depth==1 && dim==1 &&
   122                           (*this)[0]=='C'+0.5f && (*this)[1]=='I'+0.5f &&
   123                           (*this)[2]=='m'+0.5f && (*this)[3]=='g'+0.5f &&
   124                           (*this)[4]=='3'+0.5f && (*this)[5]=='d'+0.5f);
   125   if (!is_header) return false;
   126   const int
   127     nbv = (int)(*this)[6],
   128     nbp = (int)(*this)[7];
   129   if (nbv<=0 || nbp<=0) return false;
   130   const T *ptrs = ptr() + 8 + 3*nbv, *const ptre = end();
   131   if (ptrs>=ptre) return false;
   132   for (int i = 0; i<nbp && ptrs<ptre; ++i) {
   133     const int N = (int)*(ptrs++);
   134     if (N<=0 || N>=8) return false;
   135     ptrs+=N;
   136   }
   137   ptrs+=4*nbp;
   138   if (ptrs>ptre) return false;
   139   return true;
   140 }
   142 template<typename tp, typename tf, typename tc, typename to>
   143 CImg<T> get_draw_object3d(const float x0, const float y0, const float z0,
   144                           const CImg<tp>& points, const CImgList<tf>& primitives,
   145                           const CImgList<tc>& colors, const CImg<to>& opacities,
   146                           const unsigned int render_type, const bool double_sided,
   147                           const float focale, const float lightx, const float lighty,
   148                           const float lightz, const float specular_light, const float specular_shine,
   149                           float *const zbuffer) const {
   150   return (+*this).draw_object3d(x0,y0,z0,points,primitives,colors,opacities,render_type,double_sided,focale,
   151                                 lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
   152 }
   154 template<typename tp, typename tc, typename to>
   155 CImg<T>& object3dtoCImg3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
   156   if (is_empty() || !primitives) { primitives.assign(); colors.assign(); opacities.assign(); return *this; }
   157   const unsigned int primitives_size = primitives.size;
   158   CImgList<floatT> res;
   159   res.insert(CImg<floatT>("CImg3d",1,6,1,1,false)+=0.5f);
   160   res.insert(CImg<floatT>::vector((float)width,(float)primitives.size));
   161   res.insert(1); resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(res.last());
   162   cimglist_for(primitives,p) {
   163     res.insert(CImg<floatT>::vector((float)primitives[p].size())).insert(primitives[p]).last().unroll('y');
   164     primitives[p].assign();
   165   }
   166   primitives.assign();
   167   const unsigned int defined_colors = colors.size;
   168   cimglist_for(colors,c) { res.insert(colors[c]).last().resize(1,3,1,1,-1); colors[c].assign(); }
   169   colors.assign();
   170   if (defined_colors<primitives_size) res.insert(1).last().assign(1,3*(primitives_size-defined_colors),1,1,200);
   171   const unsigned int defined_opacities = opacities.size();
   172   res.insert(opacities).last().unroll('y');
   173   opacities.assign();
   174   if (defined_opacities<primitives.size) res.insert(1).last().assign(1,primitives_size-defined_opacities,1,1,1);
   175   return res.get_append('y').transfer_to(*this);
   176 }
   178 template<typename tp, typename tc, typename to>
   179 CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
   180   const T *ptrs = ptr() + 6;
   181   const unsigned int
   182     nbv = (unsigned int)*(ptrs++),
   183     nbp = (unsigned int)*(ptrs++);
   184   CImg<T> points(nbv,3);
   185   primitives.assign(nbp);
   186   colors.assign(nbp,1,3,1,1);
   187   opacities.assign(nbp);
   188   cimg_forX(points,x) { points(x,0) = (T)*(ptrs++); points(x,1) = (T)*(ptrs++); points(x,2) = (T)*(ptrs++); }
   189   cimglist_for(primitives,p) {
   190     const unsigned int N = (unsigned int)*(ptrs++);
   191     primitives[p].assign(ptrs,1,N,1,1,false);
   192     ptrs+=N;
   193   }
   194   cimglist_for(colors,c) { colors(c,0) = (tc)*(ptrs++); colors(c,1) = (tc)*(ptrs++); colors(c,2) = (tc)*(ptrs++); }
   195   opacities.assign(ptrs,1,nbp,1,1,false);
   196   return assign(points);
   197 }
   199 CImg<T> get_appendCImg3d(const CImg<T>& img) const {
   200   CImg<T> res(1,img.size() + size() - 8);
   201   const T *ptrs = ptr() + 6, *ptrs0 = img.ptr() + 6;
   202   T *ptrd = res.ptr();
   203   *(ptrd++) = (T)('C' + 0.5f); *(ptrd++) = (T)('I' + 0.5f);
   204   *(ptrd++) = (T)('m' + 0.5f); *(ptrd++) = (T)('g' + 0.5f);
   205   *(ptrd++) = (T)('3' + 0.5f); *(ptrd++) = (T)('d' + 0.5f);
   206   const unsigned int
   207     nbv = (unsigned int)*(ptrs++),
   208     nbv0 = (unsigned int)*(ptrs0++),
   209     nbp = (unsigned int)*(ptrs++),
   210     nbp0 = (unsigned int)*(ptrs0++);
   211   *(ptrd++) = (T)(nbv + nbv0);
   212   *(ptrd++) = (T)(nbp + nbp0);
   213   std::memcpy(ptrd,ptrs,sizeof(T)*nbv*3);
   214   ptrd+=3*nbv; ptrs+=3*nbv;
   215   std::memcpy(ptrd,ptrs0,sizeof(T)*nbv0*3);
   216   ptrd+=3*nbv0; ptrs0+=3*nbv0;
   217   for (unsigned int i = 0; i<nbp; ++i) {
   218     const unsigned int N = (unsigned int)*(ptrs++);
   219     *(ptrd++) = (T)N;
   220     std::memcpy(ptrd,ptrs,sizeof(T)*N);
   221     ptrd+=N; ptrs+=N;
   222   }
   223   for (unsigned int i = 0; i<nbp0; ++i) {
   224     const unsigned int N = (unsigned int)*(ptrs0++);
   225     *(ptrd++) = (T)N;
   226     for (unsigned int j = 0; j<N; ++j) *(ptrd++) = (T)(*(ptrs0++) + nbv);
   227   }
   228   std::memcpy(ptrd,ptrs,sizeof(T)*nbp*3);
   229   ptrd+=3*nbp; ptrs+=3*nbp;
   230   std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0*3);
   231   ptrd+=3*nbp0; ptrs0+=3*nbp0;
   232   std::memcpy(ptrd,ptrs,sizeof(T)*nbp);
   233   ptrd+=nbp;
   234   std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0);
   235   return res;
   236 }
   238 CImg<T>& appendCImg3d(const CImg<T>& img) {
   239   return get_appendCImg3d(img).transfer_to(*this);
   240 }
   242 CImg<T>& centerCImg3d() {
   243   const unsigned int nbv = (unsigned int)(*this)[6];
   244   const T *ptrs = ptr() + 8;
   245   float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
   246   for (unsigned int i = 0; i<nbv; ++i) {
   247     const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   248     if (x<xm) xm = x; if (x>xM) xM = x;
   249     if (y<ym) ym = y; if (y>yM) yM = y;
   250     if (z<zm) zm = z; if (z>zM) zM = z;
   251   }
   252   const float xc = (xm + xM)/2, yc = (ym + yM)/2, zc = (zm + zM)/2;
   253   T *ptrd = ptr() + 8;
   254   for (unsigned int i = 0; i<nbv; ++i) { *(ptrd++)-=(T)xc; *(ptrd++)-=(T)yc; *(ptrd++)-=(T)zc; }
   255   return *this;
   256 }
   258 CImg<T> get_centerCImg3d() const {
   259   return (+*this).centerCImg3d();
   260 }
   262 CImg<T>& normalizeCImg3d() {
   263   const unsigned int nbv = (unsigned int)(*this)[6];
   264   const T *ptrs = ptr() + 8;
   265   float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
   266   for (unsigned int i = 0; i<nbv; ++i) {
   267     const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   268     if (x<xm) xm = x; if (x>xM) xM = x;
   269     if (y<ym) ym = y; if (y>yM) yM = y;
   270     if (z<zm) zm = z; if (z>zM) zM = z;
   271   }
   272   const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
   273   if (delta>0) {
   274     T *ptrd = ptr() + 8;
   275     for (unsigned int i = 0; i<3*nbv; ++i) *(ptrd++)/=(T)delta;
   276   }
   277   return *this;
   278 }
   280 CImg<T> get_normalizeCImg3d() const {
   281   return (+*this).normalizeCImg3d();
   282 }
   284 template<typename t>
   285 CImg<T>& rotateCImg3d(const CImg<t>& rot) {
   286   const unsigned int nbv = (unsigned int)(*this)[6];
   287   const T *ptrs = ptr() + 8;
   288   const float
   289     a = (float)rot(0,0), b = (float)rot(1,0), c = (float)rot(2,0),
   290     d = (float)rot(0,1), e = (float)rot(1,1), f = (float)rot(2,1),
   291     g = (float)rot(0,2), h = (float)rot(1,2), i = (float)rot(2,2);
   292   T *ptrd = ptr() + 8;
   293   for (unsigned int j = 0; j<nbv; ++j) {
   294     const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
   295     *(ptrd++) = (T)(a*x + b*y + c*z);
   296     *(ptrd++) = (T)(d*x + e*y + f*z);
   297     *(ptrd++) = (T)(g*x + h*y + i*z);
   298   }
   299   return *this;
   300 }
   302 template<typename t>
   303 CImg<T> get_rotateCImg3d(const CImg<t>& rot) const {
   304   return (+*this).rotateCImg3d(rot);
   305 }
   307 CImg<T>& translateCImg3d(const float tx, const float ty, const float tz) {
   308   const unsigned int nbv = (unsigned int)(*this)[6];
   309   T *ptrd = ptr() + 8;
   310   for (unsigned int j = 0; j<nbv; ++j) { *(ptrd++) += (T)tx; *(ptrd++) += (T)ty; *(ptrd++) += (T)tz; }
   311   return *this;
   312 }
   314 CImg<T> get_translateCImg3d(const float tx, const float ty, const float tz) const {
   315   return (+*this).translateCImg3d(tx,ty,tz);
   316 }
   318 CImg<T>& coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) {
   319   T *ptrd = ptr() + 6;
   320   const unsigned int
   321     nbv = (unsigned int)*(ptrd++),
   322     nbp = (unsigned int)*(ptrd++);
   323   ptrd+=3*nbv;
   324   for (unsigned int i = 0; i<nbp; ++i) { const unsigned int N = (unsigned int)*(ptrd++); ptrd+=N; }
   325   if (set_RGB) for (unsigned int c = 0; c<nbp; ++c) { *(ptrd++) = (T)R; *(ptrd++) = (T)G; *(ptrd++) = (T)B; } else ptrd+=3*nbp;
   326   if (set_opacity) for (unsigned int o = 0; o<nbp; ++o) *(ptrd++) = (T)opacity;
   327   return *this;
   328 }
   330 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 {
   331   return (+*this).coloropacityCImg3d(R,G,B,opacity,set_RGB,set_opacity);
   332 }
   334 #else  // eq. to #ifndef cimg_plugin
   336 #define cimg_debug 1
   337 #ifndef cimg_gmic_cpp
   338 #define cimg_gmic_cpp "examples/gmic.cpp"
   339 #define cimg_cimg_h "../CImg.h"
   340 #endif
   341 #define cimg_stdout stdout
   342 #define cimg_plugin cimg_gmic_cpp
   343 #include cimg_cimg_h
   344 #include "gmic.h"
   345 using namespace cimg_library;
   347 // The lines below are necessary when using a non-standard compiler such as visualcpp6.
   348 #ifdef cimg_use_visualcpp6
   349 #define std
   350 #endif
   351 #ifdef min
   352 #undef min
   353 #undef max
   354 #endif
   356 #if !defined(gmic_main) || !defined(gmic_separate_compilation)
   358 // Define some useful macros.
   359 //---------------------------
   361 // Code for validity checking of indices.
   362 #define gmic_inds indices2string(indices,true)
   363 #define gmic_check_indice(ind,funcname) { \
   364   const int indo = (int)ind; \
   365   if (ind<0) ind+=images.size; \
   366   if (ind<0 || ind>=(int)images.size) { \
   367     if (images.size) error(funcname " : Invalid indice '[%d]' (valid indice range is -%u...%u).",gmic_inds,indo,images.size,images.size-1); \
   368     else error(funcname " : Invalid indice '[%d]' (image list is empty).",gmic_inds,indo); \
   369   } \
   370 }
   372 // Code for having 'get' or 'non-get' versions of G'MIC commands.
   373 #define gmic_apply(instance,function) { \
   374   if (get_version) { \
   375     unsigned int posi = 0; \
   376     if (images.contains(instance,posi)) filenames.insert(filenames[posi]); \
   377     else filenames.insert(CImg<char>("(gmic)",7,1,1,1,false)); \
   378     CImg<T> res = instance.get_##function; \
   379     images.insert(1); res.transfer_to(images.last()); \
   380   } else instance.function; \
   381 }
   383 // Code for simple commands that has no parameters and act on images.
   384 #define gmic_simple_item(option,function,description) \
   385   if (!cimg::strcmp(option,item0)) { \
   386     print(description,gmic_inds); cimg_foroff(indices,l) gmic_apply(images[indices[l]],function()); \
   387     continue; \
   388 }
   390 // Code for the type cast command.
   391 #define gmic_cast(pixel_type,st_type) \
   392   if (!cimg::strcmp(#pixel_type,argument)) { \
   393     print("Set pixel type to '%s'.",#pixel_type); ++position; \
   394     if (!cimg::strcmp(st_type,cimg::type<T>::string())) continue; \
   395     CImgList<pixel_type> casted_images; \
   396     while (images) { casted_images.insert(images[0]); images.remove(0); } \
   397     return parse_##pixel_type(casted_images); \
   398 }
   400 // Code for G'MIC arithmetic commands.
   401 #define gmic_arithmetic_item(option1,option2,\
   402                              function1,description1,arg1_1,arg1_2,value_type1, \
   403                              function2,description2_1,description2_2,arg2_1,arg2_2,description3) \
   404  if (!cimg::strcmp(option1,item0) || !cimg::strcmp(option2,item0)) { \
   405    double value = 0; char inds[4096] = { 0 }, sep = 0, end = 0; \
   406     if (std::sscanf(argument,"%lf%c",&value,&end)==1) { \
   407       print(description1 ".",arg1_1,arg1_2); \
   408       cimg_foroff(indices,l) \
   409        if (get_version) { \
   410          images.insert(images[indices[l]]); images.last().function1((value_type1)value); \
   411          filenames.insert(filenames[indices[l]]); } \
   412        else images[indices[l]].function1((value_type1)value); \
   413       ++position; \
   414     } else if (std::sscanf(argument,"[%4095[0-9.eE%+-]%c%c",inds,&sep,&end)==2 && sep==']') { \
   415       const CImg<unsigned int> ind = indices2cimg(inds,images.size,option1); \
   416       if (ind.size()!=1) error(description2_1 " : Argument '[%s]' should contain one indice.",gmic_inds,inds); \
   417       print(description2_2 ".",arg2_1,arg2_2); \
   418       const CImg<T> img0 = images[ind[0]]; \
   419       cimg_foroff(indices,l) \
   420        if (get_version) { \
   421          images.insert(images[indices[l]]); images.last().function2(img0); \
   422          filenames.insert(filenames[indices[l]]); } \
   423        else images[indices[l]].function2(img0); \
   424       ++position; \
   425     } else { \
   426       print(description3 ".",gmic_inds); \
   427       if (images && indices) { \
   428         for (unsigned int siz = indices.size(), ind0 = indices[0], off = 0, l = 1; l<siz; ++l) { \
   429           const unsigned int ind = indices[l] - off; \
   430           images[ind0].function2(images[ind]); \
   431           images.remove(ind); filenames.remove(ind); \
   432           ++off; \
   433         }}} continue; \
   434 }
   436 // Constructors.
   437 //--------------
   438 #if defined(gmic_float) || !defined(gmic_separate_compilation)
   440 #include "gmic_def.h"
   442 gmic_exception::gmic_exception() {
   443   message[0] = '\0';
   444 }
   446 gmic_exception::gmic_exception(const char *format, ...) {
   447   std::va_list ap;
   448   va_start(ap,format);
   449   std::vsprintf(message,format,ap);
   450   va_end(ap);
   451 }
   453 gmic_exception::gmic_exception(const char *format, std::va_list ap) {
   454   std::vsprintf(message,format,ap);
   455 }
   457 gmic::gmic() {
   458   assign(0);
   459 }
   461 // Set default values of G'MIC parameters and macros.
   462 //----------------------------------------------------
   463 gmic& gmic::assign(const unsigned int size, const char *const custom_macros, const bool add_macros_start) {
   464   filenames.assign(size,CImg<char>("(gmic)",7,1,1,1,false));
   465   position = 0;
   466   verbosity_level = 0;
   467   is_released = true;
   468   is_debug = false;
   469   is_begin = true;
   470   background3d[0] = 120;
   471   background3d[1] = 120;
   472   background3d[2] = 140;
   473   render3d = 4;
   474   renderd3d = -1;
   475   is_oriented3d = false;
   476   focale3d = 500;
   477   light3d_x = 0;
   478   light3d_y = 0;
   479   light3d_z = -5000;
   480   specular_light3d = 0.15f;
   481   specular_shine3d = 0.8f;
   482   is_fullpath = false;
   483   add_macros(data_def,sizeof(data_def)-1,true);
   484   add_macros(custom_macros,cimg::strlen(custom_macros)-1,add_macros_start);
   485   return *this;
   486 }
   488 // Error procedure.
   489 //-----------------
   490 const gmic& gmic::error(const char *format, ...) const {
   491   va_list ap;
   492   va_start(ap,format);
   493   char message[1024] = { 0 };
   494   std::vsprintf(message,format,ap);
   495   va_end(ap);
   496   if (verbosity_level>=0) {
   497     std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Error ** %s",filenames.size,message);
   498     std::fprintf(cimg_stdout,"\n<gmic-#%u> Abort G'MIC instance.\n",filenames.size);
   499     std::fflush(cimg_stdout);
   500   }
   501   throw gmic_exception(message);
   502   return *this;
   503 }
   505 // Warning procedure.
   506 //-------------------
   507 const gmic& gmic::warning(const char *format, ...) const {
   508   va_list ap;
   509   va_start(ap,format);
   510   if (verbosity_level>=0) {
   511     std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Warning ** ",filenames.size);
   512     std::vfprintf(cimg_stdout,format,ap);
   513     std::fflush(cimg_stdout);
   514   }
   515   va_end(ap);
   516   return *this;
   517 }
   519 // Print debug messages.
   520 //----------------------
   521 const gmic& gmic::debug(const char *format, ...) const {
   522   const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' };
   523   const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' };
   524   const char t_bold[] = { 0x1b,'[','1','m','\0' };
   525   if (is_debug) {
   526     va_list ap;
   527     va_start(ap,format);
   528     std::fprintf(cimg_stdout,"\n%s%s<gmic-debug-#%u>%s ",t_bold,t_red,filenames.size,t_normal);
   529     std::vfprintf(cimg_stdout,format,ap);
   530     va_end(ap);
   531     std::fflush(cimg_stdout);
   532   }
   533   return *this;
   534 }
   536 // Print status messages.
   537 //-----------------------
   538 const gmic& gmic::print(const char *format, ...) const {
   539   va_list ap;
   540   va_start(ap,format);
   541   if (verbosity_level>=0) {
   542     std::fprintf(cimg_stdout,"\n<gmic-#%u> ",filenames.size);
   543     std::vfprintf(cimg_stdout,format,ap);
   544     std::fflush(cimg_stdout);
   545   }
   546   va_end(ap);
   547   return *this;
   548 }
   550 // Add macros from a char* buffer.
   551 //---------------------------------
   552 gmic& gmic::add_macros(const char *const data_macros, const unsigned int data_size, const bool add_macros_at_start) {
   553   if (!data_macros || !data_size) return *this;
   554   char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
   555   const char *data = data_macros, *const data_end = data_macros + data_size;
   556   while (data<data_end) {
   557     if (*data=='\n') ++data;
   558     else {
   559       if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line) + 1;
   560       if (line[0]!='#') { // Useful line (not a comment)
   561         mac[0] = com[0] = 0;
   562         if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
   563             std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
   564           macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
   565           commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
   566         } else { // Possible continuation of a previous macro definition.
   567           if (!macros) error("Fatal error : Invalid G'MIC macros data.");
   568           CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
   569           last[last.size()-1] = ' ';
   570           last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
   571         }
   572       }
   573     }
   574   }
   575   return *this;
   576 }
   578 // Add macros from a macro file.
   579 //------------------------------
   580 gmic& gmic::add_macros(std::FILE *const file, const bool add_macros_at_start) {
   581   if (!file) return *this;
   582   char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
   583   int err = 0;
   584   while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
   585     if (err) { // Non empty-line
   586       mac[0] = com[0] = 0;
   587       if (line[0]!='#') { // Useful line (not a comment).
   588         if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
   589             std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
   590           macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
   591           commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
   592         } else { // Possible continuation of a previous macro definition.
   593           if (!macros) error("Fatal error : Invalid G'MIC macros data.");
   594           CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
   595           last[last.size()-1] = ' ';
   596           last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
   597         }
   598       }
   599     }
   600   }
   601   return *this;
   602 }
   604 // Return indices of the images from a string.
   605 //--------------------------------------------
   606 CImg<unsigned int> gmic::indices2cimg(const char *const string, const unsigned int indice_max,
   607                                       const char *const command) const {
   608   if (!cimg::strlen(string)) return CImg<unsigned int>();
   609   CImgList<unsigned int> inds;
   610   const char *it = string;
   611   for (bool stopflag = false; !stopflag; ) {
   612     char sep = 0, end = 0, item0[4096] = { 0 }, item1[4096] = { 0 };
   613     float ind0 = 0, ind1 = 0, step = 1;
   614     if (std::sscanf(it,"%4095[^,]%c",item0,&end)!=2) stopflag = true;
   615     else it += 1 + cimg::strlen(item0);
   616     const int err = std::sscanf(item0,"%4095[^:]%c%f%c",item1,&sep,&step,&end);
   617     if (err!=1 && err!=3) error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
   618     if (std::sscanf(item1,"%f%%-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%') {
   619       ind0 = (float)cimg::round(ind0*indice_max/100,1);
   620       ind1 = (float)cimg::round(ind1*indice_max/100,1);
   621     } else if (std::sscanf(item1,"%f%%-%f%c",&ind0,&ind1,&end)==2)
   622       ind0 = (float)cimg::round(ind0*indice_max/100,1);
   623     else if (std::sscanf(item1,"%f-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%')
   624       ind1 = (float)cimg::round(ind1*indice_max/100,1);
   625     else if (std::sscanf(item1,"%f-%f%c",&ind0,&ind1,&end)==2) { }
   626     else if (std::sscanf(item1,"%f%c%c",&ind0,&sep,&end)==2 && sep=='%')
   627       ind1 = (ind0 = (float)cimg::round(ind0*indice_max/100,1));
   628     else if (std::sscanf(item1,"%f%c",&ind0,&end)==1)
   629       ind1 = ind0;
   630     else error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
   631     if (ind0<0) ind0+=indice_max;
   632     if (ind1<0) ind1+=indice_max;
   633     if (ind0<0 || ind0>=indice_max || ind1<0 || ind1>=indice_max || step<=0) {
   634       if (indice_max) error("Command '%s' : Invalid indice(s) '[%s]' (valid indice range is -%u...%u).",
   635                             command,string,indice_max,indice_max-1);
   636       else error("Command '%s' : Invalid indice(s) '[%s]' (image list is empty).",
   637                  command,string);
   638     }
   639     if (ind0>ind1) cimg::swap(ind0,ind1);
   640     const unsigned int
   641       iind0 = (unsigned int)ind0,
   642       _ind1 = (unsigned int)ind1,
   643       iind1 = (unsigned int)(_ind1 - cimg::mod((float)_ind1,step));
   644     if (iind0==iind1) inds.insert(CImg<unsigned int>::vector(iind0));
   645     else inds.insert(CImg<unsigned int>::sequence((unsigned int)(1+(iind1-iind0)/step),
   646                                                   (unsigned int)iind0,
   647                                                   (unsigned int)iind1).get_split('y'));
   648   }
   649   inds = inds.get_append('y').sort().get_split('y');
   650   cimglist_for(inds,l) if (l!=inds.size-1 && inds(l,0)==inds(l+1,0)) inds.remove(l--);
   651   if (is_debug) {
   652     debug("Indices : ");
   653     inds.get_append('y').print(); // List indices if debug mode is activated.
   654   }
   655   return inds.get_append('y').sort();
   656 }
   658 // Return stringified version of indices or filenames.
   659 //----------------------------------------------------
   660 char* gmic::indices2string(const CImg<unsigned int>& indices, const bool display_indices) const {
   661   static char res0[4096] = { 0 }, res1[4096] = { 0 };
   662   const unsigned int siz = indices.size();
   663   if (display_indices) {
   664     switch (siz) {
   665     case 0: std::sprintf(res0," []"); break;
   666     case 1: std::sprintf(res0," [%u]",indices[0]); break;
   667     case 2: std::sprintf(res0,"s [%u,%u]",indices[0],indices[1]); break;
   668     case 3: std::sprintf(res0,"s [%u,%u,%u]",indices[0],indices[1],indices[2]); break;
   669     case 4: std::sprintf(res0,"s [%u,%u,%u,%u]",indices[0],indices[1],indices[2],indices[3]); break;
   670     default: std::sprintf(res0,"s [%u,...,%u]",indices[0],indices[siz-1]);
   671     }
   672     return res0;
   673   }
   674   switch (siz) {
   675   case 0: std::sprintf(res1," "); break;
   676   case 1: std::sprintf(res1,"%s",filenames[indices[0]].ptr()); break;
   677   case 2: std::sprintf(res1,"%s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr()); break;
   678   case 3: std::sprintf(res1,"%s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
   679                        filenames[indices[2]].ptr()); break;
   680   case 4: std::sprintf(res1,"%s, %s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
   681                        filenames[indices[2]].ptr(), filenames[indices[3]].ptr()); break;
   682   default: std::sprintf(res1,"%s, ..., %s",filenames[indices[0]].ptr(),filenames[indices[siz-1]].ptr());
   683   }
   684   return res1;
   685 }
   686 #endif // #if defined(gmic_float) || !defined(gmic_separate_compilation)
   688 // Template constructors.
   689 //-----------------------
   690 template<typename T>
   691 gmic::gmic(const int argc, const char *const *const argv, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
   692   assign(images.size,custom_macros,add_macros_at_start);
   693   for (int pos = 1; pos<argc; ++pos)
   694     command_line.insert(CImg<char>(argv[pos],cimg::strlen(argv[pos])+1,1,1,1,false));
   695   is_released = false;
   696   parse(images);
   697 }
   699 template<typename T>
   700 gmic::gmic(const char *const command, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
   701   assign(images.size,custom_macros,add_macros_at_start);
   702   char item[4096] = { 0 };
   703   for (const char *ncommand = command; *ncommand; ) {
   704     if (std::sscanf(ncommand,"%[^ ]",item)==1) {
   705       const int l = cimg::strlen(item);
   706       command_line.insert(CImg<char>(item,l+1,1,1,1,false));
   707       ncommand += l;
   708       while (*ncommand==' ') ++ncommand;
   709     } else break;
   710   }
   711   is_released = true;
   712   parse(images);
   713 }
   715 // Display specified image(s).
   716 //-----------------------------
   717 template<typename T>
   718 bool gmic::display_images(const CImgList<T>& images, const CImg<unsigned int>& indices,
   719                           const bool verbose) const {
   720   if (!images || !indices) { print("Display image []."); return false; }
   721   CImgList<unsigned int> inds = indices.get_unroll('x').get_split('x');
   722   CImgList<T> visu;
   723   unsigned int max_height = 0;
   724   cimglist_for(inds,l) {
   725     const CImg<T>& img = images[inds(l,0)];
   726     if (img.height>max_height && !img.is_CImg3d()) max_height = img.height;
   727   }
   728   cimglist_for(inds,l) {
   729     const unsigned int ind = inds(l,0);
   730     const CImg<T> &img = images[ind];
   731     if (img) {
   732       if (!max_height || img.height<max_height) visu.insert(img,~0U,true);
   733       else visu.insert(img.get_lines(0,max_height-1));
   734     } else if (verbose) { warning("Display image : Image [%d] is empty.",ind); inds.remove(l--); }
   735   }
   736   const CImg<unsigned int> nindices = inds.get_append('x');
   737   const char *const fnames = indices2string(nindices,false);
   738   print("Display image%s = '%s'.\n\n",gmic_inds,fnames);
   739   if (visu.size) {
   740     if (visu.size!=1) visu.display(fnames,verbosity_level>=0,'x','p');
   741     else {
   742       const CImg<T> &img = visu[0];
   743       char title[4096] = { 0 };
   744       std::sprintf(title,"%s (%dx%dx%dx%d)",fnames,img.dimx(),img.dimy(),img.dimz(),img.dimv());
   745       img.display(title,verbosity_level>=0);
   746     }
   747   }
   748   return true;
   749 }
   751 // Display plots of specified image(s).
   752 //--------------------------------------
   753 template<typename T>
   754 bool gmic::display_plots(const CImgList<T>& images, const CImg<unsigned int>& indices,
   755                          const unsigned int plot_type, const unsigned int vertex_type,
   756                          const double xmin, const double xmax,
   757                          const double ymin, const double ymax,
   758                          const bool verbose) const {
   759   if (!images || !indices) { print("Plot image []."); return false; }
   760   CImgDisplay disp(cimg_fitscreen(640,480,1),0,0);
   761   cimg_foroff(indices,l) {
   762     const unsigned int ind = indices[l];
   763     const CImg<T>& img = images[ind];
   764     if (img) {
   765       print("Plot image%s = '%s'.\n",gmic_inds,indices2string(indices,false));
   766       if (verbosity_level>=0) { std::fputc('\n',cimg_stdout); img.print(filenames[ind].ptr()); }
   767       char title[4096] = { 0 };
   768       std::sprintf(title,"%s (%dx%dx%dx%d)",
   769                    filenames[ind].ptr(),img.dimx(),img.dimy(),img.dimz(),img.dimv());
   770       img.display_graph(disp.set_title(title),plot_type,vertex_type,0,xmin,xmax,0,ymin,ymax);
   771     } else if (verbose) warning("Plot image : Image [%d] is empty.",ind);
   772   }
   773   return true;
   774 }
   776 // Display specified 3D object(s).
   777 //--------------------------------
   778 template<typename T>
   779 bool gmic::display_objects3d(const CImgList<T>& images, const CImg<unsigned int>& indices,
   780                              const bool verbose) const {
   781   if (!indices) { print("Display 3D object []."); return false; }
   782   CImg<unsigned char> background;
   783   bool exist3d = false;
   784   CImgDisplay disp;
   785   cimg_foroff(indices,l) {
   786     const unsigned int ind = indices[l];
   787     const CImg<T> &img = images[ind];
   788     if (!img.is_CImg3d()) {
   789       if (verbose) warning("Display 3D object : Image [%d] is not a 3D object.",ind);
   790     } else {
   791       exist3d = true;
   792       if (!background || !disp) {
   793         background.assign(cimg_fitscreen(640,480,1),1,3);
   794         cimg_forV(background,k) background.get_shared_channel(k).fill(background3d[k]);
   795         disp.assign(background);
   796       }
   797       CImgList<unsigned int> primitives3d;
   798       CImgList<unsigned char> colors3d;
   799       CImg<float> opacities3d;
   800       CImg<float> points3d(img);
   801       points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
   802       print("Display 3D object [%u] = '%s' (%d points, %u primitives).",
   803             ind,filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
   804       disp.set_title("%s (%d points, %u primitives)",
   805                      filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
   806       background.display_object3d(disp,points3d,primitives3d,colors3d,opacities3d,
   807                                   true,render3d,renderd3d,!is_oriented3d,focale3d,specular_light3d,specular_shine3d);
   808       if (disp.is_closed) break;
   809     }
   810   }
   811   return exist3d;
   812 }
   814 // Substitute '@' expressions.
   815 //----------------------------
   816 template<typename T>
   817 CImg<char> gmic::substitute_arobace(const char *const argument, const CImgList<T>& images) const {
   818   if (!argument) return CImg<char>();
   819   CImgList<char> _largument;
   820   char range[4096] = { 0 };
   821   for (const char *nargument = argument; *nargument; ) {
   822     if (*nargument=='@') {
   823       char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
   824       int ind = 0, bcond = 0; *range = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, sep = 0, end = 0;
   825       float x = 0, y = 0, z = 0, v = 0, m = 0, M = 1;
   826       if (nargument[1]=='#' ||
   827           (std::sscanf(nargument,"@{#%c",&sep)==1 && sep=='}')) {
   828         std::sprintf(range,"%u",images.size);
   829         _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
   830         if (sep=='}') nargument+=4; else nargument+=2;
   831       } else if (std::sscanf(nargument,"@%d",&ind)==1 ||
   832                  (std::sscanf(nargument,"@{%d%c",&ind,&sep)==2 && sep=='}') ||
   833                  (std::sscanf(nargument,"@{%d%*c%4095[^}]%c",&ind,range,&sep)==3 && sep=='}')) {
   834         int nind = ind;
   835         if (nind<0) nind+=images.size;
   836         if (nind<0 || nind>=(int)images.size) {
   837           if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
   838                                  ind,images.size,images.size-1);
   839           else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
   840         }
   841         const unsigned int sizrange = cimg::strlen(range);
   842         const CImg<T>& img = images[nind];
   843         CImg<T> values;
   844         if (sizrange) {
   845           const CImg<unsigned int> iinds = indices2cimg(range,img.size(),"parsing");
   846           values.assign(iinds.size());
   847           cimg_foroff(iinds,p) values[p] = img[iinds(p)];
   848         } else values = img.get_shared();
   849         const CImg<char> vs = values.value_string();
   850         const unsigned int vsl = vs.size();
   851         if (vsl>1) _largument.insert(CImg<char>(vs.ptr(),vsl-1,1,1,1,true));
   852         nargument+= 1 + (sep?2:0) + (sizrange?1:0) + sizrange + std::sprintf(range,"%d",ind);
   853       } else if (((std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%c",&ind,argx,&sep)==3 && sep==')') ||
   854                   (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind,argx,argy,&sep)==4 && sep==')') ||
   855                   (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
   856                                &ind,argx,argy,argz,&sep)==5 && sep==')') ||
   857                   (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",
   858                                &ind,argx,argy,argz,argv,&sep)==6 && sep==')') ||
   859                   (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",
   860                                &ind,argx,argy,argz,argv,&bcond,&sep)==7 && sep==')')) &&
   861                  (!*argx ||
   862                   (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
   863                   std::sscanf(argx,"%f%c",&x,&end)==1) &&
   864                  (!*argy ||
   865                   (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
   866                   std::sscanf(argy,"%f%c",&y,&end)==1) &&
   867                  (!*argz ||
   868                   (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
   869                   std::sscanf(argz,"%f%c",&z,&end)==1) &&
   870                  (!*argv ||
   871                   (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
   872                   std::sscanf(argv,"%f%c",&v,&end)==1)) {
   873         int nind = ind;
   874         if (nind<0) nind+=images.size;
   875         if (nind<0 || nind>=(int)images.size) {
   876           if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
   877                                  ind,images.size,images.size-1);
   878           else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
   879         }
   880         const CImg<T>& img = images[nind];
   881         const int
   882           nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
   883           ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
   884           nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
   885           nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
   886         std::sprintf(range,"%g",bcond?(double)img.atXYZV(nx,ny,nz,nv):(double)img.atXYZV(nx,ny,nz,nv,0));
   887         _largument.insert(CImg<char>(range,cimg::strlen(range)));
   888         while (*nargument!=')') ++nargument; ++nargument;
   889       } else if ((std::sscanf(nargument,"@%c",&sep)==1 && sep=='?') ||
   890                  (std::sscanf(nargument,"@{?%c",&sep)==1 && sep=='}') ||
   891                  (std::sscanf(nargument,"@[?%c",&sep)==1 && sep==']') ||
   892                  (std::sscanf(nargument,"@{?%*c%f%c",&M,&sep)==2 && sep=='}') ||
   893                  (std::sscanf(nargument,"@[?%*c%f%c",&M,&sep)==2 && sep==']') ||
   894                  (std::sscanf(nargument,"@{?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep=='}') ||
   895                  (std::sscanf(nargument,"@[?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep==']')) {
   896         const double r = m + (M-m)*cimg::rand();
   897         if (sep==']') std::sprintf(range,"%d",(int)cimg::round(r,1)); else std::sprintf(range,"%g",r);
   898         _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
   899         if (sep=='?') nargument+=2; else { while (*nargument!=sep) ++nargument; ++nargument; }
   900       } else _largument.insert(CImg<char>(nargument++,1,1,1,1,true));
   901     } else {
   902       std::sscanf(nargument,"%4095[^@]",range);
   903       const int l = cimg::strlen(range);
   904       _largument.insert(CImg<char>(range,l,1,1,1,true));
   905       nargument+=l;
   906     }
   907   }
   908   _largument.insert(CImg<char>::vector(0));
   909   return _largument.get_append('x');
   910 }
   912 // Main parsing procedure.
   913 //------------------------
   914 template<typename T>
   915 gmic& gmic::parse(CImgList<T> &images) {
   916   const unsigned int command_line_maxsize = 65535;
   917   const int no_ind = (int)(~0U>>1);
   918   cimg::exception_mode() = 0;
   920   // Begin command line parsing.
   921   while (position<command_line.size && command_line.size<command_line_maxsize) {
   922     const char
   923       *const orig_item = command_line[position].ptr(),
   924       *const orig_argument = position+1<command_line.size?command_line[position+1].ptr():"";
   926     // Substitute '@' expressions in 'orig_item' and 'orig_argument' if necessary.
   927     CImg<char> _item, _argument, _argument_text;
   928     if (*orig_item=='-' || *orig_item=='[' || *orig_item=='(') {
   929       if (std::strchr(orig_item,'@')) {
   930         _item = substitute_arobace(orig_item,images);
   931         if (_item.size()>4095) error("Buffer overflow when substituting item '%s'.",orig_item);
   932       }
   933       if (*orig_item=='-' &&
   934           (*orig_argument!='-' ||
   935            (*orig_argument=='-' && (orig_argument[1]=='.' || orig_argument[1]=='@' ||
   936                                     (orig_argument[1]>='0' && orig_argument[1]<='9'))))
   937           && std::strchr(orig_argument,'@')) {
   938         _argument = substitute_arobace(orig_argument,images);
   939         if (_argument.size()>4095) error("Buffer overflow when substituting argument '%s'.",orig_argument);
   940       }
   941     }
   942     const char
   943       *item = _item?_item.ptr():orig_item,
   944       *argument = _argument?_argument.ptr():orig_argument;
   945     const char *argument_text = argument;
   946     if (cimg::strlen(argument)>=64) {
   947       _argument_text.assign(64,1,1,1,0);
   948       std::memcpy(_argument_text.ptr(),argument,60*sizeof(char));
   949       _argument_text[60] = _argument_text[61] = _argument_text[62] = '.'; _argument_text[63] = 0;
   950       argument_text = _argument_text.ptr();
   951     }
   953     // Get current item/command from the command line.
   954     char item0[4096] = { 0 }, item1[4096] = { 0 };
   955     bool get_version = false;
   956     CImg<unsigned int> indices;
   957     if (item[0]=='-' && item[1] && item[1]!='.') {
   958       char sep0 = 0, sep1 = 0, end = 0;
   959       if (item[1]=='-' && item[2] && item[2]!='[' && item[2]!='3' && item[3]!='d') { ++item; get_version = true; }
   960       const int err = std::sscanf(item,"%4095[^[]%c%4095[0-9.eE%,:+-]%c%c",item0,&sep0,item1,&sep1,&end);
   961       if (err==1) indices = CImg<unsigned int>::sequence(images.size,0,images.size-1);
   962       else if (err==4 && sep1==']')
   963         indices = indices2cimg(item1,(!strcmp(item0,"-i")||!strcmp(item0,"-input"))?images.size+1:images.size,item0);
   964       else { std::strcpy(item0,item); item1[0] = 0; }
   965     }
   966     ++position;
   968     // Check for verbosity commands.
   969     if (*item=='-') {
   970       if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item)) ++verbosity_level;
   971       else if (!cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) --verbosity_level;
   972     }
   974     if (is_begin) { print("Start G'MIC instance."); is_begin = false; }
   975     debug("Item : '%s', Argument : '%s'.",item,argument);
   977     // Begin command interpretation.
   978     try {
   979       if (*item=='-') {
   981         //----------------
   982         // Global options
   983         //----------------
   985         // Verbosity (actually, just continue to next command since verbosity has been already processed above).
   986         if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item) ||
   987             !cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) continue;
   989         // Load macro file.
   990         if (!cimg::strcmp("-macros",item) || !cimg::strcmp("-m",item)) {
   991           print("Load macro file '%s'",cimg::basename(argument));
   992           std::FILE *const file = cimg::fopen(argument,"r");
   993           if (file) {
   994             const unsigned int siz = macros.size;
   995             add_macros(file,true);
   996             cimg::fclose(file);
   997             if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u macros added).",macros.size-siz);
   998           }
   999           else error("Load macro file '%s' : File not found.",argument);
  1000           ++position; continue;
  1003         // Switch 'is_debug' flag.
  1004         if (!cimg::strcmp("-debug",item)) {
  1005           is_debug = !is_debug;
  1006           continue;
  1009         // Switch 'is_fullpath' flag.
  1010         if (!cimg::strcmp("-fullpath",item)) {
  1011           is_fullpath = !is_fullpath;
  1012           continue;
  1015         //----------------------
  1016         // Arithmetic operators
  1017         //----------------------
  1019         // Common arithmetic operators.
  1020         gmic_arithmetic_item("-add","-+",
  1021                              operator+=,"Add %g to image%s",value,gmic_inds,T,
  1022                              operator+=,"Add to image%s",
  1023                              "Add image [%d] to image%s",ind[0],gmic_inds,
  1024                              "Add image%s together");
  1026         gmic_arithmetic_item("-sub","--",
  1027                              operator-=,"Substract %g to image%s",value,gmic_inds,T,
  1028                              operator-=,"Substract to image%s",
  1029                              "Substract image [%d] to image%s",ind[0],gmic_inds,
  1030                              "Substract image%s together");
  1032         gmic_arithmetic_item("-mul","-*",
  1033                              operator*=,"Multiply image%s by %g",gmic_inds,value,double,
  1034                              mul,"Multiply image%s",
  1035                              "Multiply image%s by image [%d]",gmic_inds,ind[0],
  1036                              "Multiply image%s together");
  1038         gmic_arithmetic_item("-div","-/",
  1039                              operator/=,"Divide image%s by %g",gmic_inds,value,double,
  1040                              div,"Divide image%s",
  1041                              "Divide image%s by image [%d]",gmic_inds,ind[0],
  1042                              "Divide image%s together");
  1044         gmic_arithmetic_item("-pow","-^",
  1045                              pow,"Compute image%s to the power of %g",gmic_inds,value,double,
  1046                              pow,"Compute power of image%s",
  1047                              "Compute image%s to the power of image [%d]",gmic_inds,ind[0],
  1048                              "Compute the power of image%s together");
  1050         gmic_arithmetic_item("-min","-min",
  1051                              min,"Compute pointwise minimum between image%s and %g",gmic_inds,value,T,
  1052                              min,"Compute pointwise minimum with image%s",
  1053                              "Compute pointwise minimum between image%s and image [%d]",gmic_inds,ind[0],
  1054                              "Compute pointwise minimum between image%s together");
  1056         gmic_arithmetic_item("-max","-max",
  1057                              max,"Compute pointwise maximum between image%s and %g",gmic_inds,value,T,
  1058                              max,"Compute pointwise maximum with image%s",
  1059                              "Compute pointwise maximum between image%s and image [%d]",gmic_inds,ind[0],
  1060                              "Compute pointwise maximum between image%s together");
  1062         gmic_arithmetic_item("-mod","-%",
  1063                              operator%=,"Compute pointwise modulo between image%s and %g.",gmic_inds,value,T,
  1064                              operator%=,"Compute pointwise modulo with image%s",
  1065                              "Compute pointwise modulo between image%s and image [%d]",gmic_inds,ind[0],
  1066                              "Compute pointwise modulo between image%s together");
  1068         gmic_arithmetic_item("-and","-and",
  1069                              operator&=,"Compute bitwise AND between image%s and %g.",gmic_inds,value,unsigned int,
  1070                              operator&=,"Compute bitwise AND with image%s",
  1071                              "Compute bitwise AND between image%s and image [%d]",gmic_inds,ind[0],
  1072                              "Compute bitwise AND between image%s together");
  1074         gmic_arithmetic_item("-or","-or",
  1075                              operator|=,"Compute bitwise OR between image%s and %g.",gmic_inds,value,unsigned int,
  1076                              operator|=,"Compute bitwise OR with image%s",
  1077                              "Compute bitwise OR between image%s and image [%d]",gmic_inds,ind[0],
  1078                              "Compute bitwise OR between image%s together");
  1080         gmic_arithmetic_item("-xor","-xor",
  1081                              operator^=,"Compute bitwise XOR between image%s and %g.",gmic_inds,value,unsigned int,
  1082                              operator^=,"Compute bitwise XOR with image%s",
  1083                              "Compute bitwise XOR between image%s and image [%d]",gmic_inds,ind[0],
  1084                              "Compute bitwise XOR between image%s together");
  1086         // Other arithmetic operators.
  1087         gmic_simple_item("-cos",cos,"Compute cosine of image%s.");
  1088         gmic_simple_item("-sin",sin,"Compute sine of image%s.");
  1089         gmic_simple_item("-tan",tan,"Compute tangent of image%s.");
  1090         gmic_simple_item("-acos",acos,"Compute arccosine of image%s.");
  1091         gmic_simple_item("-asin",asin,"Compute arcsine of image%s.");
  1092         gmic_simple_item("-atan",atan,"Compute arctangent of image%s.");
  1093         gmic_simple_item("-abs",abs,"Compute absolute value of image%s.");
  1094         gmic_simple_item("-sqr",sqr,"Compute square function of image%s.");
  1095         gmic_simple_item("-sqrt",sqrt,"Compute square root of image%s.");
  1096         gmic_simple_item("-exp",exp,"Compute exponential of image%s.");
  1097         gmic_simple_item("-log",log,"Compute logarithm of image%s.");
  1098         gmic_simple_item("-log10",log10,"Compute logarithm_10 of image%s.");
  1100         //---------------------------------------
  1101         // Pointwise operations on pixel values
  1102         //---------------------------------------
  1104         // Type cast.
  1105         if (!cimg::strcmp("-type",item) || !cimg::strcmp("-t",item)) {
  1106           typedef unsigned char uchar;
  1107           typedef unsigned short ushort;
  1108           typedef unsigned int uint;
  1109 #ifndef gmic_minimal
  1110           gmic_cast(bool,"bool");
  1111           gmic_cast(uchar,"unsigned char");
  1112           gmic_cast(char,"char");
  1113           gmic_cast(ushort,"unsigned short");
  1114           gmic_cast(short,"short");
  1115           gmic_cast(uint,"unsigned int");
  1116           gmic_cast(int,"int");
  1117           gmic_cast(double,"double");
  1118 #endif
  1119           gmic_cast(float,"float");
  1120           error("Set pixel type : Invalid argument '%s' "
  1121                 "(should be '{bool,uchar,char,ushort,short,uint,int,float,double}').",
  1122                 argument_text);
  1125         // Set scalar value.
  1126         if (!cimg::strcmp("-set",item0) || !cimg::strcmp("-=",item0)) {
  1127           double value = 0; float x = 0, y = 0, z = 0, v = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
  1128           char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
  1129           if ((std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1130                std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%c",&value,argx,&end)==2 ||
  1131                std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,&end)==3 ||
  1132                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 ||
  1133                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) &&
  1134               (!*argx ||
  1135                (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
  1136                std::sscanf(argx,"%f%c",&x,&end)==1) &&
  1137               (!*argy ||
  1138                (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
  1139                std::sscanf(argy,"%f%c",&y,&end)==1) &&
  1140               (!*argz ||
  1141                (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
  1142                std::sscanf(argz,"%f%c",&z,&end)==1) &&
  1143               (!*argv ||
  1144                (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
  1145                std::sscanf(argv,"%f%c",&v,&end)==1)) {
  1146             print("Set scalar value %g at position (%g%s,%g%s,%g%s,%g%s) in image%s",
  1147                   value,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",v,sepv=='%'?"%":"",gmic_inds);
  1148             cimg_foroff(indices,l) {
  1149               CImg<T> &img = images[indices[l]];
  1150               const int
  1151                 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  1152                 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  1153                 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
  1154                 nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
  1155               gmic_apply(images[indices[l]],gmic_set(value,nx,ny,nz,nv));
  1157           } else error("Set scalar value in image%s : Invalid argument '%s' "
  1158                        "(should be 'value,x[,y[,z[,v]]]').",
  1159                        argument_text);
  1160           ++position; continue;
  1163         // Invert endianness.
  1164         gmic_simple_item("-endian",invert_endianness,"Invert endianness of image%s.");
  1166         // Fill.
  1167         if (!cimg::strcmp("-fill",item0) || !cimg::strcmp("-f",item0)) {
  1168           char sep = 0, end = 0; int ind0 = no_ind;
  1169           if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  1170             gmic_check_indice(ind0,"Fill image%s");
  1171             print("Fill image%s with values of image [%d].",gmic_inds,ind0);
  1172             const CImg<T> values = images[ind0];
  1173             cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(values));
  1174           } else {
  1175             print("Fill image%s with values '%s'.",gmic_inds,argument_text);
  1176             cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(argument,true));
  1178           ++position; continue;
  1181         // Threshold.
  1182         if (!cimg::strcmp("-threshold",item0)) {
  1183           char sep = 0, end = 0; int soft = 0; double value = 0;
  1184           if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1185               (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%') ||
  1186               std::sscanf(argument,"%lf%*c%d%c",&value,&soft,&end)==2 ||
  1187               (std::sscanf(argument,"%lf%c%*c%d%c",&value,&sep,&soft,&end)==3 && sep=='%')) {
  1188             print("Threshold image%s with %g%s (%s threshold).",gmic_inds,value,sep=='%'?"%":"",soft?"soft":"hard");
  1189             cimg_foroff(indices,l) {
  1190               CImg<T> &img = images[indices[l]];
  1191               double vmin = 0, vmax = 0, nvalue = value;
  1192               if (sep=='%') { vmin = img.minmax(vmax); nvalue = vmin + (vmax - vmin)*value/100; }
  1193               gmic_apply(img,threshold((T)nvalue,soft?true:false));
  1195             ++position;
  1196           } else {
  1197             print("Threshold image%s : Interactive mode.",gmic_inds);
  1198             CImgDisplay disp;
  1199             char title[4096] = { 0 };
  1200             cimg_foroff(indices,l) {
  1201               CImg<T>
  1202                 &img = images[indices[l]],
  1203                 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
  1204                 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
  1205               if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
  1206               else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
  1207               double
  1208                 vmin = 0, vmax = (double)img.maxmin(vmin),
  1209                 distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0)),
  1210                 amount = 50;
  1211               bool stopflag = false, obutt = false;
  1212               int omx = -1, omy = -1;
  1213               CImg<T> res;
  1214               for (disp.show().button = disp.key = 0; !stopflag; ) {
  1215                 const unsigned int key = disp.key;
  1216                 if (!res) {
  1217                   std::sprintf(title,"%s : Interactive threshold %.3g%%",filenames[indices[l]].ptr(),amount);
  1218                   disp.display(res=visu.get_threshold((T)(vmin + amount*(vmax-vmin)/100))).
  1219                     set_title(title).wait();
  1221                 const int mx = disp.mouse_x, my = disp.mouse_y;
  1222                 if (disp.button && mx>=0 && my>=0) {
  1223                   if (omx==mx && omy==my && !obutt) break;
  1224                   omx = mx; omy = my; obutt = true;
  1225                   const double dist = std::sqrt((double)cimg::sqr(mx) + cimg::sqr(my));
  1226                   amount = dist*100/distmax;
  1227                   res.assign();
  1228                 } else if (!disp.button) obutt = false;
  1229                 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
  1230                 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
  1231                     (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
  1232                   disp.is_resized = true;
  1233                 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
  1234                     (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
  1235                   disp.is_resized = true;
  1236                 if (disp.is_resized) {
  1237                   disp.resize(false).display(res);
  1238                   distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0));
  1241               gmic_apply(img,threshold((T)(vmin + amount*(vmax-vmin)/100)));
  1244           continue;
  1247         // Cut.
  1248         if (!cimg::strcmp("-cut",item0)) {
  1249           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1250           double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1251           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1252               ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
  1253                (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1254                std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
  1255               ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
  1256                (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1257                std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
  1258             if (ind0!=no_ind) { gmic_check_indice(ind0,"Cut image%s"); value0 = images[ind0].min(); sep0 = 0; }
  1259             if (ind1!=no_ind) { gmic_check_indice(ind1,"Cut image%s"); value1 = images[ind1].max(); sep1 = 0; }
  1260             print("Cut image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
  1261             cimg_foroff(indices,l) {
  1262               CImg<T> &img = images[indices[l]];
  1263               double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
  1264               if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
  1265               if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
  1266               gmic_apply(img,cut((T)nvalue0,(T)nvalue1));
  1268             ++position;
  1269           } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
  1270             if (ind0!=no_ind) gmic_check_indice(ind0,"Cut image%s");
  1271             value0 = images[ind0].minmax(value1);
  1272             print("Cut image%s in [%g,%g].",gmic_inds,value0,value1);
  1273             cimg_foroff(indices,l) gmic_apply(images[indices[l]],cut((T)value0,(T)value1));
  1274             ++position;
  1275           } else {
  1276             print("Cut image%s : Interactive mode.",gmic_inds);
  1277             CImgDisplay disp;
  1278             char title[4096] = { 0 };
  1279             cimg_foroff(indices,l) {
  1280               CImg<T>
  1281                 &img = images[indices[l]],
  1282                 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
  1283                 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
  1284               if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
  1285               else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
  1286               double vmin = 0, vmax = (double)img.maxmin(vmin), amount0 = 0, amount1 = 100;
  1287               bool stopflag = false, obutt = false;
  1288               int omx = -1, omy = -1;
  1289               CImg<T> res;
  1290               for (disp.show().button = disp.key = 0; !stopflag; ) {
  1291                 const unsigned int key = disp.key;
  1292                 if (!res) {
  1293                   std::sprintf(title,"%s : Interactive cut [%.3g%%,%.3g%%]",
  1294                                filenames[indices[l]].ptr(),amount0,amount1);
  1295                   disp.display(res = visu.get_cut((T)(vmin + amount0*(vmax-vmin)/100),
  1296                                                   (T)(vmin + amount1*(vmax-vmin)/100))).
  1297                     set_title(title).wait();
  1299                 const int mx = disp.mouse_x, my = disp.mouse_y;
  1300                 if (disp.button && mx>=0 && my>=0) {
  1301                   if (omx==mx && omy==my && !obutt) break;
  1302                   omx = mx; omy = my; obutt = true;
  1303                   amount0 = mx*100/disp.dimx(); amount1 = my*100/disp.dimy();
  1304                   res.assign();
  1305                 } else if (!disp.button) obutt = false;
  1306                 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
  1307                 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
  1308                     (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
  1309                   disp.is_resized = true;
  1310                 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
  1311                     (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
  1312                   disp.is_resized = true;
  1313                 if (disp.is_resized) disp.resize(false).display(res);
  1315               gmic_apply(img,cut((T)(vmin + amount0*(vmax-vmin)/100),(T)(vmin + amount1*(vmax-vmin)/100)));
  1318           continue;
  1321         // Normalize.
  1322         if (!cimg::strcmp("-normalize",item0) || !cimg::strcmp("-n",item0)) {
  1323           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1324           double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1325           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1326               ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
  1327                (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1328                std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
  1329               ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
  1330                (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1331                std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
  1332             if (ind0!=no_ind) { gmic_check_indice(ind0,"Normalize image%s"); value0 = images[ind0].min(); sep0 = 0; }
  1333             if (ind1!=no_ind) { gmic_check_indice(ind1,"Normalize image%s"); value1 = images[ind1].max(); sep1 = 0; }
  1334             print("Normalize image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
  1335             cimg_foroff(indices,l) {
  1336               CImg<T> &img = images[indices[l]];
  1337               double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
  1338               if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
  1339               if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
  1340               gmic_apply(img,normalize((T)nvalue0,(T)nvalue1));
  1342           } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
  1343             if (ind0!=no_ind) gmic_check_indice(ind0,"Normalize image%s");
  1344             value0 = images[ind0].minmax(value1);
  1345             print("Normalize image%s in [%g,%g].",gmic_inds,value0,value1);
  1346             cimg_foroff(indices,l) gmic_apply(images[indices[l]],normalize((T)value0,(T)value1));
  1347           } else error("Normalize image%s : Invalid argument '%s' "
  1348                        "(should be '{value1[%%],[indice]},{value2[%%],[indice]}').",gmic_inds,argument_text);
  1349           ++position; continue;
  1352         // Round.
  1353         if (!cimg::strcmp("-round",item0)) {
  1354           char end = 0; double value = 0; int rtype = 0;
  1355           if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  1356               std::sscanf(argument,"%lf%*c%d%c",&value,&rtype,&end)==2) {
  1357             print("Round image%s with value %g (%s rounding).",
  1358                   gmic_inds,value,rtype<0?"backward":rtype>0?"forward":"nearest");
  1359             cimg_foroff(indices,l) gmic_apply(images[indices[l]],round((float)value,rtype));
  1360           } else error("Round image%s : Invalid argument '%s' "
  1361                        "(should be 'round_value[,round_type]').",gmic_inds,argument_text);
  1362           ++position; continue;
  1365         // Equalize histogram.
  1366         if (!cimg::strcmp("-equalize",item0)) {
  1367           float nb = 256; char sep = 0, end = 0;
  1368           if (std::sscanf(argument,"%f%c",&nb,&end)==1 ||
  1369               (std::sscanf(argument,"%f%c%c",&nb,&sep,&end)==2 && sep=='%')) {
  1370             if (nb<=0) error("Equalize image%s : Invalid cluster number %g.",gmic_inds,nb);
  1371             print("Equalize image%s with %g%s clusters.",gmic_inds,nb,sep=='%'?"%":"");
  1372             cimg_foroff(indices,l) {
  1373               CImg<T>& img = images[indices[l]];
  1374               unsigned int N = (unsigned int)nb;
  1375               if (sep=='%') { double m, M = img.maxmin(m); N = (unsigned int)cimg::round((M-m)*nb/100,1); }
  1376               gmic_apply(img,equalize((int)nb));
  1378           } else error("Equalize image%s : Invalid argument '%s' "
  1379                        "(should be 'nb_clusters[%%]').",gmic_inds,argument_text);
  1380           ++position; continue;
  1383         // Quantize.
  1384         if (!cimg::strcmp("-quantize",item0)) {
  1385           int nb = 0; char end = 0;
  1386           if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1387             print("Quantize image%s with %d levels.",gmic_inds,nb);
  1388             cimg_foroff(indices,l) gmic_apply(images[indices[l]],quantize(nb));
  1389           } else error("Quantize image%s : Invalid argument '%s' "
  1390                        "(should be 'nb_levels').",gmic_inds,argument_text);
  1391           ++position; continue;
  1394         // Add noise.
  1395         if (!cimg::strcmp("-noise",item0)) {
  1396           float sigma = 0; char sep = 0, end = 0; int ntype = 0;
  1397           if (std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
  1398               (std::sscanf(argument,"%f%c%c",&sigma,&sep,&end)==2 && sep=='%') ||
  1399               std::sscanf(argument,"%f%*c%d%c",&sigma,&ntype,&end)==2 ||
  1400               (std::sscanf(argument,"%f%c%*c%d%c",&sigma,&sep,&ntype,&end)==3 && sep=='%')) {
  1401             const char *st_type = ntype==0?"gaussian":ntype==1?"uniform":ntype==2?"salt&pepper":"poisson";
  1402             if (sep=='%') sigma = -sigma;
  1403             print("Add %s noise with standard deviation %g%s to image%s.",
  1404                   st_type,cimg::abs(sigma),sigma<0?"%":"",gmic_inds);
  1405             cimg_foroff(indices,l) gmic_apply(images[indices[l]],noise(sigma,ntype));
  1406           } else error("Add noise to image%s : Invalid argument '%s' "
  1407                        "(should be 'std[%%][,noise_type]').",gmic_inds,argument_text);
  1408           ++position; continue;
  1411         // Rand.
  1412         if (!cimg::strcmp("-rand",item0)) {
  1413           double value0 = 0, value1 = 0; char end = 0;
  1414           if (std::sscanf(argument,"%lf%*c%lf%c",&value0,&value1,&end)==2) {
  1415             print("Fill image%s with random values in [%g,%g].",gmic_inds,value0,value1);
  1416             cimg_foroff(indices,l) gmic_apply(images[indices[l]],rand((T)value0,(T)value1));
  1417           } else error("Fill image%s with random values : Invalid argument '%s' "
  1418                        "(should be 'valmin,valmax').",gmic_inds,argument_text);
  1419           ++position; continue;
  1422         // Compute pointwise norms and orientations.
  1423         gmic_simple_item("-norm",pointwise_norm,"Compute vector norm.");
  1424         gmic_simple_item("-orientation",pointwise_orientation,"Compute vector orientation.");
  1426         //------------------------
  1427         // Color base conversions
  1428         //------------------------
  1429         gmic_simple_item("-rgb2hsv",RGBtoHSV,"Convert image%s from RGB to HSV colorbases.");
  1430         gmic_simple_item("-rgb2hsl",RGBtoHSL,"Convert image%s from RGB to HSL colorbases.");
  1431         gmic_simple_item("-rgb2hsi",RGBtoHSI,"Convert image%s from RGB to HSI colorbases.");
  1432         gmic_simple_item("-rgb2yuv",RGBtoYUV,"Convert image%s from RGB to YUV colorbases.");
  1433         gmic_simple_item("-rgb2ycbcr",RGBtoYCbCr,"Convert image%s from RGB to YCbCr colorbases.");
  1434         gmic_simple_item("-rgb2xyz",RGBtoXYZ,"Convert image%s from RGB to XYZ colorbases.");
  1435         gmic_simple_item("-rgb2lab",RGBtoLab,"Convert image%s from RGB to Lab colorbases.");
  1436         gmic_simple_item("-rgb2cmy",RGBtoCMY,"Convert image%s from RGB to CMY colorbases.");
  1437         gmic_simple_item("-rgb2cmyk",RGBtoCMYK,"Convert image%s from RGB to CMYK colorbases.");
  1438         gmic_simple_item("-cmyk2rgb",CMYKtoRGB,"Convert image%s from CMYK to RGB colorbases.");
  1439         gmic_simple_item("-cmy2rgb",CMYtoRGB,"Convert image%s from CMY to RGB colorbases.");
  1440         gmic_simple_item("-lab2rgb",LabtoRGB,"Convert image%s from Lab to RGB colorbases.");
  1441         gmic_simple_item("-xyz2rgb",XYZtoRGB,"Convert image%s from XYZ to RGB colorbases.");
  1442         gmic_simple_item("-ycbcr2rgb",YCbCrtoRGB,"Convert image%s from YCbCr to RGB colorbases.");
  1443         gmic_simple_item("-yuv2rgb",YUVtoRGB,"Convert image%s from YUV to RGB colorbases.");
  1444         gmic_simple_item("-hsi2rgb",HSItoRGB,"Convert image%s from HSI to RGB colorbases.");
  1445         gmic_simple_item("-hsl2rgb",HSLtoRGB,"Convert image%s from HSL to RGB colorbases.");
  1446         gmic_simple_item("-hsv2rgb",HSVtoRGB,"Convert image%s from HSV to RGB colorbases.");
  1448         // Apply LUT.
  1449         if (!cimg::strcmp("-lut2rgb",item0)) {
  1450           int nb = 0, ind0 = 0; char sep = 0, end = 0;
  1451           if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  1452             gmic_check_indice(ind0,"Map LUT on image%s");
  1453             print("Map LUT [%d] on image%s.",ind0,gmic_inds);
  1454             const CImg<T> palette = images[ind0];
  1455             cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().LUTtoRGB(palette));
  1456           } else if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1457             if (nb<0 || nb>2) error("Map LUT on image%s : Invalid LUT number %d.",gmic_inds,nb);
  1458             print("Map %s LUT on image%s.",nb==0?"default":nb==1?"rainbow":"cluster",gmic_inds);
  1459             cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().
  1460                                               LUTtoRGB(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8()));
  1461           } else error("Map LUT on image%s : Invalid argument '%s' "
  1462                        "(should be '[indice]' or '{0,1,2}').",gmic_inds,argument_text);
  1463           ++position; continue;
  1466         // Convert to LUT.
  1467         if (!cimg::strcmp("-rgb2lut",item0)) {
  1468           int nb = 0, ind0 = 0, dithering = 0; char sep = 0, end = 0;
  1469           if (std::sscanf(argument,"[%d%c%*c%d",&ind0,&sep,&dithering)>=2 && sep==']') {
  1470             gmic_check_indice(ind0,"Index image%s with LUT");
  1471             print("Index image%s with LUT [%d] %s dithering.",gmic_inds,ind0,dithering?"with":"without");
  1472             const CImg<T> palette = images[ind0];
  1473             cimg_foroff(indices,l) gmic_apply(images[indices[l]],RGBtoLUT(palette,dithering,true));
  1474           } else if (std::sscanf(argument,"%d%*c%d%c",&nb,&dithering,&end)==2 ||
  1475                      std::sscanf(argument,"%d%c",&nb,&end)==1) {
  1476             if (nb<0 || nb>2) error("Index image%s with LUT : Invalid LUT number %d.",gmic_inds,nb);
  1477             print("Index image%s with %s LUT.",gmic_inds,nb==0?"default":nb==1?"rainbow":"cluster");
  1478             cimg_foroff(indices,l) gmic_apply(images[indices[l]],
  1479                                               RGBtoLUT(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8(),
  1480                                                        dithering,true));
  1481           } else error("Index image%s with LUT : Invalid argument '%s' "
  1482                        "(should be '[indice][,dithering]', or '{0,1,2}[,dithering]').",gmic_inds,argument_text);
  1483           ++position; continue;
  1486         //-----------------------
  1487         // Geometric manipulation
  1488         //-----------------------
  1490         // Resize.
  1491         if (!cimg::strcmp("-resize",item0) || !cimg::strcmp("-r",item0)) {
  1492           char
  1493             sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0,
  1494             argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
  1495             float valx = 0, valy = 0, valz = 0, valv = 0;
  1496             int interpolation = 1, borders = -1, center = 0, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
  1497             if ((std::sscanf(argument,"[%d%c%c",&indx,&sepx,&end)==2 ||
  1498                  std::sscanf(argument,"[%d%c%*c%d%c",&indx,&sepx,&interpolation,&end)==3 ||
  1499                  std::sscanf(argument,"[%d%c%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&end)==4 ||
  1500                  std::sscanf(argument,"[%d%c%*c%d%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&center,&end)==5)
  1501                 && sepx==']') {
  1502               gmic_check_indice(indx,"Resize image%s");
  1503               const int
  1504                 ivalx = images[indx].dimx(),
  1505                 ivaly = images[indx].dimy(),
  1506                 ivalz = images[indx].dimz(),
  1507                 ivalv = images[indx].dimv();
  1508               print("Resize image%s to %dx%dx%dx%d with %s interpolation.",
  1509                     gmic_inds,ivalx,ivaly,ivalz,ivalv,
  1510                     interpolation==0?"no":interpolation==1?"nearest neighbor":
  1511                     interpolation==2?"moving average":interpolation==3?"linear":
  1512                     interpolation==4?"grid":"cubic");
  1513               cimg_foroff(indices,l) gmic_apply(images[indices[l]],resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
  1514               ++position;
  1515             } 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",
  1516                                      argx,argy,argz,argv,&(interpolation=1),&(borders=-1),&(center=0),&end)==7 ||
  1517                          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",
  1518                                      argx,argy,argz,argv,&interpolation,&borders,&end)==6 ||
  1519                          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",
  1520                                      argx,argy,argz,argv,&interpolation,&end)==5 ||
  1521                          std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1522                                      argx,argy,argz,argv,&end)==4) &&
  1523                         ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1524                          (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1525                          std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1526                         ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1527                          (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1528                          std::sscanf(argy,"%f%c",&valy,&end)==1) &&
  1529                         ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
  1530                          (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
  1531                          std::sscanf(argz,"%f%c",&valz,&end)==1) &&
  1532                         ((std::sscanf(argv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']') ||
  1533                          (std::sscanf(argv,"%f%c%c",&valv,&sepv,&end)==2 && sepv=='%') ||
  1534                          std::sscanf(argv,"%f%c",&valv,&end)==1)) ||
  1535                        ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1536                                      argx,argy,argz,&end)==3) &&
  1537                         ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1538                          (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1539                          std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1540                         ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1541                          (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1542                          std::sscanf(argy,"%f%c",&valy,&end)==1) &&
  1543                         ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
  1544                          (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
  1545                          std::sscanf(argz,"%f%c",&valz,&end)==1)) ||
  1546                        ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  1547                                      argx,argy,&end)==2) &&
  1548                         ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1549                          (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1550                          std::sscanf(argx,"%f%c",&valx,&end)==1) &&
  1551                         ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
  1552                          (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
  1553                          std::sscanf(argy,"%f%c",&valy,&end)==1)) ||
  1554                        ((std::sscanf(argument,"%4095[][0-9.eE%+-]%c",
  1555                                      argx,&end)==1) &&
  1556                         ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
  1557                          (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
  1558                          std::sscanf(argx,"%f%c",&valx,&end)==1))) {
  1559               if (indx!=no_ind) { gmic_check_indice(indx,"Resize image%s"); valx = (float)images[indx].dimx(); sepx = 0; }
  1560               if (indy!=no_ind) { gmic_check_indice(indy,"Resize image%s"); valy = (float)images[indy].dimy(); sepy = 0; }
  1561               if (indz!=no_ind) { gmic_check_indice(indz,"Resize image%s"); valz = (float)images[indz].dimz(); sepz = 0; }
  1562               if (indv!=no_ind) { gmic_check_indice(indv,"Resize image%s"); valv = (float)images[indv].dimv(); sepv = 0; }
  1563               if (!valx) { valx = 100; sepx = '%'; }
  1564               if (!valy) { valy = 100; sepy = '%'; }
  1565               if (!valz) { valz = 100; sepz = '%'; }
  1566               if (!valv) { valv = 100; sepv = '%'; }
  1567               print("Resize image%s to %g%s%g%s%g%s%g%s with %s interpolation.",
  1568                     gmic_inds,valx,sepx=='%'?"%x":"x",valy,sepy=='%'?"%x":"x",valz,
  1569                     sepz=='%'?"%x":"x",valv,sepv=='%'?"% ":" ",
  1570                     interpolation==0?"no":interpolation==1?"nearest neighbor":
  1571                     interpolation==2?"moving average":interpolation==3?"linear":
  1572                     interpolation==4?"grid":"cubic");
  1574               cimg_foroff(indices,l) {
  1575                 CImg<T>& img = images[indices[l]];
  1576                 const int
  1577                   ivalx0 = (int)cimg::round(sepx=='%'?valx*img.dimx()/100:valx,1),
  1578                   ivaly0 = (int)cimg::round(sepy=='%'?valy*img.dimy()/100:valy,1),
  1579                   ivalz0 = (int)cimg::round(sepz=='%'?valz*img.dimz()/100:valz,1),
  1580                   ivalv0 = (int)cimg::round(sepv=='%'?valv*img.dimv()/100:valv,1),
  1581                   ivalx = ivalx0?ivalx0:1,
  1582                   ivaly = ivaly0?ivaly0:1,
  1583                   ivalz = ivalz0?ivalz0:1,
  1584                   ivalv = ivalv0?ivalv0:1;
  1585                 gmic_apply(img,resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
  1587               ++position;
  1588             } else {
  1589               print("Resize image%s : Interactive mode.",gmic_inds);
  1590               char title[4096] = { 0 };
  1591               cimg_foroff(indices,l) {
  1592                 CImg<T>& img = images[indices[l]];
  1593                 CImgDisplay disp(img,0,1);
  1594                 std::sprintf(title,"%s : Interactive resize",filenames[indices[l]].ptr());
  1595                 disp.set_title(title);
  1596                 img.get_select(0,disp);
  1597                 print("Resize image [%d] to %dx%d.",indices[l],disp.dimx(),disp.dimy());
  1598                 gmic_apply(img,resize(disp));
  1601             continue;
  1604         // Resize2x. and Resize3x.
  1605         gmic_simple_item("-resize2x",resize_doubleXY,"Resize image%s using Scale2x algorithm.");
  1606         gmic_simple_item("-resize3x",resize_doubleXY,"Resize image%s using Scale3x algorithm.");
  1608         // Crop.
  1609         if (!cimg::strcmp("-crop",item0) || !cimg::strcmp("-c",item0)) {
  1610           char st0[4096] = { 0 }, st1[4096] = { 0 }, st2[4096] = { 0 }, st3[4096] = { 0 };
  1611           char st4[4096] = { 0 }, st5[4096] = { 0 }, st6[4096] = { 0 }, st7[4096] = { 0 };
  1612           char sep0 = 0, sep1 = 0, sep2 = 0, sep3 = 0, sep4 = 0, sep5 = 0, sep6 = 0, sep7 = 0, end = 0;
  1613           float a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0; int borders = 0;
  1614           if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1615                            "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1616                            st0,st1,st2,st3,st4,st5,st6,st7,&borders,&end)==9 ||
  1617                std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1618                            "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1619                            st0,st1,st2,st3,st4,st5,st6,st7,&end)==8) &&
  1620               (std::sscanf(st0,"%f%c",&a0,&end)==1 || (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1621               (std::sscanf(st1,"%f%c",&a1,&end)==1 || (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1622               (std::sscanf(st2,"%f%c",&a2,&end)==1 || (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1623               (std::sscanf(st3,"%f%c",&a3,&end)==1 || (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
  1624               (std::sscanf(st4,"%f%c",&a4,&end)==1 || (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
  1625               (std::sscanf(st5,"%f%c",&a5,&end)==1 || (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%')) &&
  1626               (std::sscanf(st6,"%f%c",&a6,&end)==1 || (std::sscanf(st6,"%f%c%c",&a6,&sep6,&end)==2 && sep6=='%')) &&
  1627               (std::sscanf(st7,"%f%c",&a7,&end)==1 || (std::sscanf(st7,"%f%c%c",&a7,&sep7,&end)==2 && sep7=='%'))) {
  1628             print("Crop image%s with (%g%s%g%s%g%s%g%s x (%g%s%g%s%g%s%g%s.",gmic_inds,
  1629                   a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",
  1630                   a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")",
  1631                   a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%,":",",
  1632                   a6,sep6=='%'?"%,":",",a7,sep7=='%'?"%)":")");
  1633             cimg_foroff(indices,l) {
  1634               CImg<T> &img = images[indices[l]];
  1635               const int
  1636                 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1637                 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1638                 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
  1639                 v0 = (int)cimg::round(sep3=='%'?a3*img.dimv()/100:a3,1),
  1640                 x1 = (int)cimg::round(sep4=='%'?a4*img.dimx()/100:a4,1),
  1641                 y1 = (int)cimg::round(sep5=='%'?a5*img.dimy()/100:a5,1),
  1642                 z1 = (int)cimg::round(sep6=='%'?a6*img.dimz()/100:a6,1),
  1643                 v1 = (int)cimg::round(sep7=='%'?a7*img.dimv()/100:a7,1);
  1644               gmic_apply(img,crop(x0,y0,z0,v0,x1,y1,z1,v1,borders?true:false));
  1646             ++position;
  1647           } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1648                                   "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1649                                   st0,st1,st2,st3,st4,st5,&borders,&end)==7 ||
  1650                       std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1651                                   "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1652                                   st0,st1,st2,st3,st4,st5,&end)==6) &&
  1653                      (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1654                       (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1655                      (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1656                       (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1657                      (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
  1658                       (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1659                      (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
  1660                       (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
  1661                      (std::sscanf(st4,"%f%c",&a4,&end)==1 ||
  1662                       (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
  1663                      (std::sscanf(st5,"%f%c",&a5,&end)==1 ||
  1664                       (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%'))) {
  1665             print("Crop image%s with (%g%s%g%s%g%s x (%g%s%g%s%g%s.",gmic_inds,
  1666                   a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",a2,sep2=='%'?"%)":")",
  1667                   a3,sep3=='%'?"%,":",",a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%)":")");
  1668             cimg_foroff(indices,l) {
  1669               CImg<T> &img = images[indices[l]];
  1670               const int
  1671                 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1672                 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1673                 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
  1674                 x1 = (int)cimg::round(sep3=='%'?a3*img.dimx()/100:a3,1),
  1675                 y1 = (int)cimg::round(sep4=='%'?a4*img.dimy()/100:a4,1),
  1676                 z1 = (int)cimg::round(sep5=='%'?a5*img.dimz()/100:a5,1);
  1677               gmic_apply(img,crop(x0,y0,z0,x1,y1,z1,borders?true:false));
  1679             ++position;
  1680           } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1681                                   "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
  1682                                   st0,st1,st2,st3,&borders,&end)==5 ||
  1683                       std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
  1684                                   "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1685                                   st0,st1,st2,st3,&end)==4) &&
  1686                      (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1687                       (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1688                      (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1689                       (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
  1690                      (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
  1691                       (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
  1692                      (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
  1693                       (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%'))) {
  1694             print("Crop image%s with (%g%s%g%s x (%g%s%g%s.",gmic_inds,
  1695                   a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%)":")",
  1696                   a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")");
  1697             cimg_foroff(indices,l) {
  1698               CImg<T> &img = images[indices[l]];
  1699               const int
  1700                 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1701                 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
  1702                 x1 = (int)cimg::round(sep2=='%'?a2*img.dimx()/100:a2,1),
  1703                 y1 = (int)cimg::round(sep3=='%'?a3*img.dimy()/100:a3,1);
  1704               gmic_apply(img,crop(x0,y0,x1,y1,borders?true:false));
  1706             ++position;
  1707           } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",st0,st1,&borders,&end)==3 ||
  1708                       std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",st0,st1,&end)==2) &&
  1709                      (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
  1710                       (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
  1711                      (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
  1712                       (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%'))) {
  1713             print("Crop image%s with (%g%s x (%g%s.",gmic_inds,
  1714                   a0,sep0=='%'?"%)":")",a1,sep1=='%'?"%)":")");
  1715             cimg_foroff(indices,l) {
  1716               CImg<T> &img = images[indices[l]];
  1717               const int
  1718                 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
  1719                 x1 = (int)cimg::round(sep1=='%'?a1*img.dimx()/100:a1,1);
  1720               gmic_apply(img,crop(x0,x1,borders?true:false));
  1722             ++position;
  1723           } else {
  1724             print("Crop image%s : Interactive mode.",gmic_inds);
  1725             char title[4096] = { 0 };
  1726             cimg_foroff(indices,l) {
  1727               CImg<T>& img = images[indices[l]];
  1728               CImgDisplay disp(cimg_fitscreen(img.dimx(),img.dimy(),1),0,1);
  1729               std::sprintf(title,"%s : Interactive crop",filenames[indices[l]].ptr());
  1730               disp.set_title(title);
  1731               const CImg<int> s = img.get_select(disp,2);
  1732               print("Crop image [%d] with (%d,%d,%d) x (%d,%d,%d).",
  1733                     indices[l],s[0],s[1],s[2],s[3],s[4],s[5]);
  1734               gmic_apply(img,crop(s[0],s[1],s[2],s[3],s[4],s[5]));
  1737           continue;
  1740         // Autocrop.
  1741         if (!cimg::strcmp("-autocrop",item0)) {
  1742           print("Autocrop image%s with color '%s'.",gmic_inds,argument_text);
  1743           cimg_foroff(indices,l) {
  1744             CImg<T>& img = images[indices[l]];
  1745             const CImg<T> col = CImg<T>(img.dimv()).fill(argument,true);
  1746             gmic_apply(img,autocrop(col));
  1748           ++position; continue;
  1751         // Select channels.
  1752         if (!cimg::strcmp("-channels",item0)) {
  1753           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1754           float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1755           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1756               ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1757                (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1758                std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1759               ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1760                (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1761                std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1762             if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channels of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
  1763             if (ind1!=no_ind) { gmic_check_indice(ind1,"Keep channels of image%s"); value1 = images[ind1].dimv()-1.0f; sep1 = 0; }
  1764             print("Keep channels %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1765             cimg_foroff(indices,l) {
  1766               CImg<T> &img = images[indices[l]];
  1767               const int
  1768                 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1),
  1769                 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,1);
  1770               gmic_apply(img,channels(nvalue0,nvalue1));
  1772           } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1773                      ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1774                       (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1775                       std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1776             if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channel of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
  1777             print("Keep channel %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1778             cimg_foroff(indices,l) {
  1779               CImg<T> &img = images[indices[l]];
  1780               const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1);
  1781               gmic_apply(img,channel(nvalue0));
  1783           } else error("Keep channels of image%s : Invalid argument '%s' "
  1784                        "(should be 'channel0[%%][,channel1[%%]]').",gmic_inds,argument_text);
  1785           ++position; continue;
  1788         // Select slices.
  1789         if (!cimg::strcmp("-slices",item0)) {
  1790           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1791           float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1792           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1793               ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1794                (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1795                std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1796               ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1797                (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1798                std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1799             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slices of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
  1800             if (ind1!=no_ind) { gmic_check_indice(ind1,"Select slices of image%s"); value1 = images[ind1].dimz()-1.0f; sep1 = 0; }
  1801             print("Select slices %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1802             cimg_foroff(indices,l) {
  1803               CImg<T> &img = images[indices[l]];
  1804               const int
  1805                 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,0),
  1806                 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,0);
  1807               gmic_apply(img,slices(nvalue0,nvalue1));
  1809           } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1810                      ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1811                       (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1812                       std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1813             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slice of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
  1814             print("Select slice %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1815             cimg_foroff(indices,l) {
  1816               CImg<T> &img = images[indices[l]];
  1817               const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimz()-1)/100:value0,1);
  1818               gmic_apply(img,slice(nvalue0));
  1820           } else error("Select slices of image%s : Invalid argument '%s' "
  1821                        "(should be 'slice0[%%][,slice1[%%]]').",gmic_inds,argument_text);
  1822           ++position; continue;
  1825         // Select lines.
  1826         if (!cimg::strcmp("-lines",item0) || !cimg::strcmp("-l",item0)) {
  1827           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1828           float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1829           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1830               ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1831                (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1832                std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1833               ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1834                (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1835                std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1836             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
  1837             if (ind1!=no_ind) { gmic_check_indice(ind1,"Select lines of image%s"); value1 = images[ind1].dimy()-1.0f; sep1 = 0; }
  1838             print("Select lines %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1839             cimg_foroff(indices,l) {
  1840               CImg<T> &img = images[indices[l]];
  1841               const int
  1842                 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1),
  1843                 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimy()-1)/100:value1,1);
  1844               gmic_apply(img,lines(nvalue0,nvalue1));
  1846           } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1847                      ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1848                       (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1849                       std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1850             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
  1851             print("Select lines %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1852             cimg_foroff(indices,l) {
  1853               CImg<T> &img = images[indices[l]];
  1854               const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1);
  1855               gmic_apply(img,line(nvalue0));
  1857           } else error("Select lines of image%s : Invalid argument '%s' "
  1858                        "(should be 'line0[%%][,line1[%%]]').",gmic_inds,argument_text);
  1859           ++position; continue;
  1862         // Columns.
  1863         if (!cimg::strcmp("-columns",item0)) {
  1864           char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
  1865           float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
  1866           if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
  1867               ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1868                (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1869                std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
  1870               ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
  1871                (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
  1872                std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
  1873             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
  1874             if (ind1!=no_ind) { gmic_check_indice(ind1,"Select columns of image%s"); value1 = images[ind1].dimx()-1.0f; sep1 = 0; }
  1875             print("Select columns %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
  1876             cimg_foroff(indices,l) {
  1877               CImg<T> &img = images[indices[l]];
  1878               const int
  1879                 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1),
  1880                 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimx()-1)/100:value1,1);
  1881               gmic_apply(img,lines(nvalue0,nvalue1));
  1883           } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
  1884                      ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
  1885                       (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  1886                       std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
  1887             if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
  1888             print("Select columns %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
  1889             cimg_foroff(indices,l) {
  1890               CImg<T> &img = images[indices[l]];
  1891               const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1);
  1892               gmic_apply(img,line(nvalue0));
  1894           } else error("Select columns of image%s : Invalid argument '%s' "
  1895                        "(should be 'column0[%%][,column1[%%]]').",gmic_inds,argument_text);
  1896           ++position; continue;
  1899         // Rotate.
  1900         if (!cimg::strcmp("-rotate",item0)) {
  1901           float angle = 0; int borders = 0, interpolation = 1; char end = 0;
  1902           if (std::sscanf(argument,"%f%c",&angle,&end)==1 ||
  1903               std::sscanf(argument,"%f%*c%d%c",&angle,&borders,&end)==2 ||
  1904               std::sscanf(argument,"%f%*c%d%*c%d%c",&angle,&borders,&interpolation,&end)==3) {
  1905             print("Rotate image%s with an angle of %g deg and %s interpolation.",
  1906                   gmic_inds,angle,interpolation?"linear":"nearest-neighbor");
  1907             if (borders>=0) { cimg_foroff(indices,l) gmic_apply(images[indices[l]],rotate(angle,borders,interpolation)); }
  1908             else cimg_foroff(indices,l) {
  1909               CImg<T> &img = images[indices[l]];
  1910               gmic_apply(img,rotate(angle,img.dimx()/2.0f,img.dimy()/2.0f,1,-1-borders,interpolation));
  1912           } else error("Rotate image%s : Invalid argument '%s' "
  1913                        "(should be 'angle[,border_conditions[,interpolation]]').",gmic_inds,argument_text);
  1914           ++position;
  1915           continue;
  1918         // Mirror.
  1919         if (!cimg::strcmp("-mirror",item0)) {
  1920           const char axis = cimg::uncase(*argument);
  1921           if (cimg::strlen(argument)==1) {
  1922             print("Mirror image%s along the %c-axis.",gmic_inds,axis);
  1923             cimg_foroff(indices,l) gmic_apply(images[indices[l]],mirror(axis));
  1924           } else error("Mirror image%s : Invalid argument '%s' "
  1925                        "(should be '{x,y,z,v}').",gmic_inds,argument_text);
  1926           ++position; continue;
  1929         // Translate.
  1930         if (!cimg::strcmp("-translate",item0)) {
  1931           char stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
  1932           char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
  1933           float dx = 0, dy = 0, dz = 0, dv = 0; int borders = 0;
  1934           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",
  1935                             stx,sty,stz,stv,&borders,&end)==5 ||
  1936                 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
  1937                             stx,sty,stz,stv,&end)==4) &&
  1938                (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1939                (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
  1940                (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%')) &&
  1941                (std::sscanf(stv,"%f%c",&dv,&end)==1 || (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%'))) ||
  1942               (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
  1943                (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1944                (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
  1945                (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%'))) ||
  1946               (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,&end)==2 &&
  1947                (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
  1948                (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%'))) ||
  1949               (std::sscanf(argument,"%4095[0-9.eE%+-]%c",stx,&end)==1 &&
  1950                (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')))) {
  1951             print("Translate image%s with vector (%g%s,%g%s,%g%s,%g%s).",
  1952                   gmic_inds,dx,sepx=='%'?"%":"",dy,sepy=='%'?"%":"",dz,sepz=='%'?"%":"",dv,sepv=='%'?"%":"");
  1953             cimg_foroff(indices,l) {
  1954               CImg<T> &img = images[indices[l]];
  1955               const int
  1956                 ndx = (int)cimg::round(sepx=='%'?dx*img.dimx()/100:dx,1),
  1957                 ndy = (int)cimg::round(sepy=='%'?dy*img.dimy()/100:dy,1),
  1958                 ndz = (int)cimg::round(sepz=='%'?dz*img.dimz()/100:dz,1),
  1959                 ndv = (int)cimg::round(sepv=='%'?dv*img.dimv()/100:dv,1);
  1960               gmic_apply(images[indices[l]],translate(ndx,ndy,ndz,ndv,borders));
  1962           } else error("Translate image%s : Invalid argument '%s' "
  1963                        "(should be 'tx[%%][,ty[%%][,tz[%%][,tv[%%][,border_conditions]]]]').",gmic_inds,argument_text);
  1964           ++position; continue;
  1967         // Transpose.
  1968         gmic_simple_item("-transpose",transpose,"Transpose image%s.");
  1970         // Invert.
  1971         gmic_simple_item("-invert",invert,"Compute matrix inversion of image%s.");
  1973         // Permute axes.
  1974         if (!cimg::strcmp("-permute",item0)) {
  1975           print("Permute axes of image%s with permutation '%s'.",gmic_inds,argument_text);
  1976           cimg_foroff(indices,l) gmic_apply(images[indices[l]],permute_axes(argument));
  1977           ++position; continue;
  1980         // Unroll.
  1981         if (!cimg::strcmp("-unroll",item0)) {
  1982           const char axis = cimg::uncase(*argument);
  1983           if (cimg::strlen(argument)==1 && (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
  1984             print("Unroll image%s along the %c-axis.",gmic_inds,axis);
  1985             cimg_foroff(indices,l) gmic_apply(images[indices[l]],unroll(axis));
  1986           } else error("Unroll image%s : Invalid argument '%s' "
  1987                        "(should be '{x,y,z,v}').",gmic_inds,argument_text);
  1988           ++position; continue;
  1991         // Split image(s).
  1992         if (!cimg::strcmp("-split",item0) || !cimg::strcmp("-s",item0)) {
  1993           char axis = cimg::uncase(*argument), foo = 0, end = 0; int nb = 0, keep_value = 0; double value = 0;
  1994           if ((std::sscanf(argument,"%c%c",&foo,&end)==1 ||
  1995                std::sscanf(argument,"%c%*c%d%c",&foo,&nb,&end)==2) &
  1996               (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
  1997             if (nb<0) error("Split image%s along the %c-axis in %d part : Invalid number of parts.",
  1998                             gmic_inds,axis,nb);
  1999             if (nb>0) print("Split image%s along the %c-axis in %d parts.",gmic_inds,axis,nb);
  2000             else print("Split image%s along the %c-axis.",gmic_inds,axis);
  2001             unsigned int off = 0;
  2002             cimg_foroff(indices,l) {
  2003               const unsigned int ind = indices[l] + off;
  2004               const CImg<T>& img = images[ind];
  2005               const CImg<char> filename = filenames[ind];
  2006               const CImgList<T> split = img.get_split(axis,nb);
  2007               if (get_version) {
  2008                 images.insert(split);
  2009                 filenames.insert(split.size,filename);
  2010               } else {
  2011                 images.remove(ind); images.insert(split,ind);
  2012                 filenames.remove(ind); filenames.insert(split.size,filename,ind);
  2013                 off+=split.size-1;
  2016           } else if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  2017                      std::sscanf(argument,"%lf%*c%d%c",&value,&keep_value,&end)==2) {
  2018             print("Split image%s according to value %g.",gmic_inds,value);
  2019             unsigned int off = 0;
  2020             cimg_foroff(indices,l) {
  2021               const unsigned int ind = indices[l] + off;
  2022               CImg<T>& img = images[ind];
  2023               const CImg<char> filename = filenames[ind];
  2024               const CImgList<T> split = img.get_split((T)value,keep_value,false);
  2025               if (get_version) {
  2026                 images.insert(split);
  2027                 filenames.insert(split.size,filename);
  2028               } else {
  2029                 images.remove(ind); images.insert(split,ind);
  2030                 filenames.remove(ind); filenames.insert(split.size,filename,ind);
  2031                 off+=split.size-1;
  2034           } else error("Split image%s : Invalid argument '%s' "
  2035                        "(should be 'axis[,nb_parts]' where 'axis' can be '{x,y,z,v}').",gmic_inds,argument_text);
  2036           ++position; continue;
  2039         // Append image(s).
  2040         if (!cimg::strcmp("-append",item0) || !cimg::strcmp("-a",item0)) {
  2041           char axis = 0, align='p', end = 0;
  2042           if ((std::sscanf(argument,"%c%c",&axis,&end)==1 ||
  2043                std::sscanf(argument,"%c%*c%c%c",&axis,&align,&end)==2)) {
  2044             axis = cimg::uncase(axis);
  2045             print("Append image%s along the %c-axis with %s alignment.",
  2046                   gmic_inds,axis,align=='p'?"left":align=='c'?"center":"right");
  2047             CImgList<T> subimages; cimg_foroff(indices,l) subimages.insert(images[indices[l]],~0U,true);
  2048             if (get_version) {
  2049               images.insert(subimages.get_append(axis,align));
  2050               filenames.insert(filenames[indices[0]]);
  2051             } else {
  2052               images.insert(subimages.get_append(axis,align),indices[0]);
  2053               filenames.insert(filenames[indices[0]],indices[0]);
  2054               int off = 1;
  2055               cimg_foroff(indices,l) {
  2056                 const int ind = indices[l] + off;
  2057                 images.remove(ind); filenames.remove(ind);
  2058                 --off;
  2061           } else error("Append image%s : Invalid argument '%s' "
  2062                        "(should be 'axis[,alignement]' where 'axis' can be '{x,y,z,v}' "
  2063                        "and alignement '{p,c,n}').",gmic_inds,argument_text);
  2064           ++position; continue;
  2067         // Warp image(s).
  2068         if (!cimg::strcmp("-warp",item0)) {
  2069           int ind0 = no_ind, interpolation = 1, relative = 0, nb = 1, borders = 1; char end = 0, sep = 0;
  2070           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')||
  2071               std::sscanf(argument,"[%d]%*c%d%c",&ind0,&relative,&end)==2 ||
  2072               std::sscanf(argument,"[%d]%*c%d%*c%d%c",&ind0,&relative,&interpolation,&end)==3 ||
  2073               std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&end)==4 ||
  2074               std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&nb,&end)==5) {
  2075             gmic_check_indice(ind0,"Warp image%s");
  2076             if (nb!=1) print("Warp image%s with %s field [%u] and %d frames.",
  2077                              gmic_inds,relative?"relative":"absolute",ind0,nb);
  2078             else print("Warp image%s with %s field [%u].",gmic_inds,relative?"relative":"absolute",ind0);
  2079             if (nb>=1) {
  2080               const CImg<T> warp = images[ind0];
  2081               unsigned int off = 0;
  2082               cimg_foroff(indices,l) {
  2083                 const unsigned int ind = indices[l] + off;
  2084                 CImg<T> &img = images[ind];
  2085                 CImgList<T> frames(nb);
  2086                 cimglist_for(frames,t) {
  2087                   const CImg<T> nwarp = warp.get_resize(img.dimx(),img.dimy(),img.dimz(),warp.dimv(),3)*=(t+1.0f)/nb;
  2088                   frames[t] = img.get_warp(nwarp,relative?true:false,interpolation?true:false,borders);
  2090                 if (get_version) {
  2091                   images.insert(frames);
  2092                   filenames.insert(nb-1,filenames[ind]);
  2093                 } else {
  2094                   images.remove(ind); images.insert(frames,ind);
  2095                   filenames.insert(nb-1,filenames[ind],ind);
  2096                   off+=nb-1;
  2100           } else error("Warp image%s : Invalid argument '%s' "
  2101                        "(should be '[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]').",
  2102                        gmic_inds,argument_text);
  2103           ++position; continue;
  2106         //-----------------------
  2107         // Image filtering
  2108         //-----------------------
  2110         // Gaussian blur.
  2111         if (!cimg::strcmp("-blur",item0)) {
  2112           float sigma = -1; int borders = 1; char end = 0;
  2113           if ((std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
  2114                std::sscanf(argument,"%f%*c%d%c",&sigma,&borders,&end)==2)
  2115               && sigma>=0) {
  2116             print("Blur image%s with standard deviation %g.",gmic_inds,sigma);
  2117             cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur(sigma,borders?true:false));
  2118           } else error("Blur image%s : Invalid argument '%s' "
  2119                        "(should be 'stdev[,border_conditions]', with stdev>=0).",gmic_inds,argument_text);
  2120           ++position; continue;
  2123         // Bilateral filter.
  2124         if (!cimg::strcmp("-bilateral",item0)) {
  2125           float sigmas = 0, sigmar = 0; char end = 0;
  2126           if (std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2) {
  2127             print("Apply bilateral filter on image%s with standart deviations %g and %g.",
  2128                   gmic_inds,sigmas,sigmar);
  2129             cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_bilateral(sigmas,sigmar));
  2130           } else error("Apply bilateral filter on image%s : Invalid argument '%s' "
  2131                        "(should be 'stdevs,stdevr').",gmic_inds,argument_text);
  2132           ++position; continue;
  2135         // Smooth.
  2136         if (!cimg::strcmp("-smooth",item0)) {
  2137           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;
  2138           unsigned int interpolation_type = 0, fast_approx = 1;
  2139           char end = 0;
  2140           if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
  2141               std::sscanf(argument,"%f%*c%f%c",&amplitude,&sharpness,&end)==2 ||
  2142               std::sscanf(argument,"%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&end)==3 ||
  2143               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&end)==4 ||
  2144               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&end)==5 ||
  2145               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&end)==6 ||
  2146               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 ||
  2147               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",
  2148                           &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&end)==8 ||
  2149               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%c",
  2150                           &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&end)==9 ||
  2151               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%*c%u%c",
  2152                           &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&fast_approx,&end)==10) {
  2153             print("Smooth image%s anisotropically with "
  2154                   "amplitude %g, sharpness %g, anisotropy %g, alpha %g and sigma %g.",
  2155                   gmic_inds,amplitude,sharpness,anisotropy,alpha,sigma);
  2156             cimg_foroff(indices,l)
  2157               gmic_apply(images[indices[l]],blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,
  2158                                                              dl,da,gauss_prec,interpolation_type,fast_approx?true:false));
  2159           } else error("Smooth image%s anisotropically : Invalid argument '%s' "
  2160                        "(should be 'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]').",
  2161                        gmic_inds,argument_text);
  2162           ++position; continue;
  2165         // Patch averaging.
  2166         if (!cimg::strcmp("-denoise",item0)) {
  2167           float sigmas = 10, sigmar = 10; int psize = 5, rsize = 6; char end = 0;
  2168           if (std::sscanf(argument,"%f%c",&sigmas,&end)==1 ||
  2169               std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2 ||
  2170               std::sscanf(argument,"%f%*c%f%*c%d%c",&sigmas,&sigmar,&psize,&end)==3 ||
  2171               std::sscanf(argument,"%f%*c%f%*c%d%*c%d%c",&sigmas,&sigmar,&psize,&rsize,&end)==4) {
  2172             if (sigmas<0 || sigmar<0 || psize<0 || rsize<0)
  2173               error("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d : "
  2174                     "Invalid parameters.",gmic_inds,psize,psize,sigmas,sigmar,rsize);
  2175             print("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d.",
  2176                   gmic_inds,psize,psize,sigmas,sigmar,rsize);
  2177             cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_patch(psize,sigmas,sigmar,rsize));
  2178           } else error("Denoise image%s : Invalid argument '%s' "
  2179                        "(should be 'stdev_s[,stdev_p[,patch_size[,lookup_size]]]').",
  2180                        gmic_inds,argument_text);
  2181           ++position; continue;
  2184         // Median filter.
  2185         if (!cimg::strcmp("-median",item0)) {
  2186           int siz = 3; char end = 0;
  2187           if (std::sscanf(argument,"%d%c",&siz,&end)==1) {
  2188             if (siz<=0) error("Apply median filter on image%s : Invalid size %d.",gmic_inds,siz);
  2189             print("Apply median filter of size %d on image%s.",siz,gmic_inds);
  2190             cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_median(siz));
  2191           } else error("Apply median filter on image%s : Invalid argument '%s' "
  2192                        "(should be 'size').",gmic_inds,argument_text);
  2193           ++position; continue;
  2196         // Sharpen.
  2197         if (!cimg::strcmp("-sharpen",item0)) {
  2198           float amplitude = 0, edge = 1, alpha = 0, sigma = 0; int sharpen_type = 0; char end = 0;
  2199           if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
  2200               std::sscanf(argument,"%f%*c%d%c",&amplitude,&sharpen_type,&end)==2 ||
  2201               std::sscanf(argument,"%f%*c%d%*c%f%c",&amplitude,&sharpen_type,&edge,&end)==3 ||
  2202               std::sscanf(argument,"%f%*c%d%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&end)==4 ||
  2203               std::sscanf(argument,"%f%*c%d%*c%f%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&sigma,&end)==5) {
  2204             if (sharpen_type)
  2205               print("Sharpen image%s with shock filters and amplitude %g, edge %g, alpha %g and sigma %g.",
  2206                     gmic_inds,amplitude,edge,alpha,sigma);
  2207             else
  2208               print("Sharpen image%s with inverse diffusion and amplitude %g.",gmic_inds,amplitude);
  2209             cimg_foroff(indices,l) gmic_apply(images[indices[l]],sharpen(amplitude,sharpen_type?true:false,edge,alpha,sigma));
  2210           } else error("Sharpen image%s : Invalid argument '%s' "
  2211                        "(should be 'amplitude[,sharpen_type[,edge[,alpha[,sigma]]]]', "
  2212                        "where 'sharpen_type' can be '{0=inverse diffusion, 1=shock filters}').",
  2213                        gmic_inds,argument_text);
  2214           ++position; continue;
  2217         // Convolve.
  2218         if (!cimg::strcmp("-convolve",item0)) {
  2219           int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  2220           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2221               std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  2222             gmic_check_indice(ind0,"Convolve image%s");
  2223             print("Convolve image%s with mask [%d].",gmic_inds,ind0);
  2224             const CImg<T> mask = images[ind0];
  2225             cimg_foroff(indices,l) gmic_apply(images[indices[l]],convolve(mask,borders));
  2226           } else error("Convolve image%s : Invalid argument '%s' "
  2227                        "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
  2228           ++position; continue;
  2231         // Correlate.
  2232         if (!cimg::strcmp("-correlate",item0)) {
  2233           int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  2234           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2235               std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  2236             gmic_check_indice(ind0,"Correlate image%s");
  2237             print("Correlate image%s with mask [%d].",gmic_inds,ind0);
  2238             const CImg<T> mask = images[ind0];
  2239             cimg_foroff(indices,l) gmic_apply(images[indices[l]],correlate(mask,borders));
  2240           } else error("Correlate image%s : Invalid argument '%s' "
  2241                        "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
  2242           ++position; continue;
  2245         // Erode.
  2246         if (!cimg::strcmp("-erode",item0)) {
  2247           int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  2248           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2249               std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  2250             gmic_check_indice(ind0,"Erode image%s");
  2251             print("Erode image%s with mask [%d].",gmic_inds,ind0);
  2252             const CImg<T> mask = images[ind0];
  2253             cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(mask,borders));
  2254           } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
  2255                      std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
  2256             if (siz<=0) error("Erode image%s : Invalid size %d.",gmic_inds,siz);
  2257             print("Erode image%s with size %d.",gmic_inds,siz);
  2258             cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(siz,borders));
  2259           } else error("Erode image%s : Invalid argument '%s' "
  2260                        "(should be '[indice]' or 'size').",gmic_inds,argument_text);
  2261           ++position; continue;
  2264         // Dilate.
  2265         if (!cimg::strcmp("-dilate",item0)) {
  2266           int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
  2267           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2268               std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
  2269             gmic_check_indice(ind0,"Dilate image%s");
  2270             print("Dilate image%s with mask [%d].",gmic_inds,ind0);
  2271             const CImg<T> mask = images[ind0];
  2272             cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(mask,borders));
  2273           } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
  2274                      std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
  2275             if (siz<=0) error("Dilate image%s : Invalid size %d.",gmic_inds,siz);
  2276             print("Dilate image%s with size %d.",gmic_inds,siz);
  2277             cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(siz,borders));
  2278           } else error("Dilate image%s : Invalid argument '%s' "
  2279                        "(should be '[indice]' or 'size').",gmic_inds,argument_text);
  2280           ++position; continue;
  2283         // Compute gradient.
  2284         if (!cimg::strcmp("-gradient",item0)) {
  2285           char axes[4096] = { 0 }, *naxes = 0, end = 0; int scheme = 3;
  2286           print("Compute gradient of image%s.",gmic_inds);
  2287           if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1 ||
  2288               std::sscanf(argument,"%4095[xyz]%*c%d%c",axes,&scheme,&end)==2) { naxes = axes; ++position; }
  2289           unsigned int off = 0;
  2290           cimg_foroff(indices,l) {
  2291             const unsigned int ind = indices[l] + off;
  2292             CImg<T>& img = images[ind];
  2293             const CImg<char> filename = filenames[ind];
  2294             const CImgList<T> gradient = img.get_gradient(naxes,scheme);
  2295             if (get_version) {
  2296               images.insert(gradient);
  2297               filenames.insert(gradient.size,filename);
  2298             } else {
  2299               images.remove(ind); images.insert(gradient,ind);
  2300               filenames.remove(ind); filenames.insert(gradient.size,filename,ind);
  2301               off+=gradient.size-1;
  2304           continue;
  2307         // Compute Hessian.
  2308         if (!cimg::strcmp("-hessian",item0)) {
  2309           char axes[4096] = { 0 }, *naxes = 0, end = 0;
  2310           print("Compute Hessian of image%s.",gmic_inds);
  2311           if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1) { naxes = axes; ++position; }
  2312           unsigned int off = 0;
  2313           cimg_foroff(indices,l) {
  2314             const unsigned int ind = indices[l] + off;
  2315             CImg<T>& img = images[ind];
  2316             const CImg<char> filename = filenames[ind];
  2317             const CImgList<T> hessian = img.get_hessian(naxes);
  2318             if (get_version) {
  2319               images.insert(hessian);
  2320               filenames.insert(hessian.size,filename);
  2321             } else {
  2322               images.remove(ind); images.insert(hessian,ind);
  2323               filenames.remove(ind); filenames.insert(hessian.size,filename,ind);
  2324               off+=hessian.size-1;
  2327           continue;
  2330         // Compute direct or inverse FFT.
  2331         const bool inv_fft = !cimg::strcmp("-ifft",item0);
  2332         if (!cimg::strcmp("-fft",item0) || inv_fft) {
  2333           print("Compute %sFourier Transform of complex data",inv_fft?"inverse ":"");
  2334           cimg_foroff(indices,l) {
  2335             const unsigned int ind0 = indices[l], ind1 = l+1<_maxl?indices[l+1]:~0U;
  2336             if (ind1!=~0U) {
  2337               if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],[%u])%c",ind0,ind1,l==_maxl-1?'.':',');
  2338               CImgList<T> fft(images[ind0],images[ind1],!get_version);
  2339               fft.FFT(inv_fft);
  2340               if (get_version) {
  2341                 images.insert(2);
  2342                 fft[0].transfer_to(images[images.size-2]);
  2343                 fft[1].transfer_to(images[images.size-1]);
  2344                 filenames.insert(filenames[ind0]);
  2345                 filenames.insert(filenames[ind1]);
  2346               } else {
  2347                 fft[0].transfer_to(images[ind0]);
  2348                 fft[1].transfer_to(images[ind1]);
  2350               ++l;
  2351             } else {
  2352               if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],0)",ind0);
  2353               CImgList<T> fft(images[ind0],!get_version);
  2354               fft.insert(fft[0],~0U,false);
  2355               fft[1].fill(0);
  2356               fft.FFT(inv_fft);
  2357               if (get_version) {
  2358                 images.insert(2);
  2359                 fft[0].transfer_to(images[images.size-2]);
  2360                 fft[1].transfer_to(images[images.size-1]);
  2361                 filenames.insert(2,filenames[ind0]);
  2362               } else {
  2363                 fft[0].transfer_to(images[ind0]);
  2364                 images.insert(fft[1],1+ind0);
  2365                 filenames.insert(filenames[ind0],1+ind0);
  2369           continue;
  2372         //-----------------------------
  2373         // Image creation and drawing
  2374         //-----------------------------
  2376         // Dimensions.
  2377         if (!cimg::strcmp("-dimensions",item0)) {
  2378           print("Get dimensions of image%s.",gmic_inds);
  2379           cimg_foroff(indices,l) {
  2380             CImg<T>& img = images[indices[l]];
  2381             CImg<int> dims = CImg<int>::vector(img.dimx(),img.dimy(),img.dimz(),img.dimv());
  2382             gmic_apply(img,replace(dims));
  2384           continue;
  2387         // Stats.
  2388         if (!cimg::strcmp("-stats",item0)) {
  2389           print("Get statistics of image%s.",gmic_inds);
  2390           cimg_foroff(indices,l) gmic_apply(images[indices[l]],stats());
  2391           continue;
  2394         // Histogram.
  2395         if (!cimg::strcmp("-histogram",item0)) {
  2396           int nb_levels = 256; char sep = 0, end = 0;
  2397           if (std::sscanf(argument,"%d%c",&nb_levels,&end)==1 ||
  2398               (std::sscanf(argument,"%d%c%c",&nb_levels,&sep,&end)==2 && sep=='%')) {
  2399             print("Compute histogram of image%s using %d%s levels.",gmic_inds,nb_levels,sep=='%'?"%":"");
  2400             cimg_foroff(indices,l) {
  2401               CImg<T> &img = images[indices[l]];
  2402               int nnb_levels = nb_levels;
  2403               if (sep=='%') { double m, M = img.maxmin(m); nnb_levels = (int)cimg::round(nb_levels*(1+M-m)/100,1); }
  2404               gmic_apply(images[indices[l]],histogram(nnb_levels));
  2406           } else error("Compute histogram of image%s : Invalid argument '%s' "
  2407                        "(should be 'nb_levels[%%]').",gmic_inds,argument_text);
  2408           ++position; continue;
  2411         // Distance function.
  2412         if (!cimg::strcmp("-distance",item0)) {
  2413           double value = 0; char sep = 0, end = 0;
  2414           if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
  2415               (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%')) {
  2416             print("Compute distance map of image%s to isovalue %g%s.",gmic_inds,value,sep=='%'?"%":"");
  2417             cimg_foroff(indices,l) {
  2418               CImg<T> &img = images[indices[l]];
  2419               double isovalue = value;
  2420               if (sep=='%') { double m, M = img.maxmin(m); isovalue = m + value*(M - m)/100; }
  2421               gmic_apply(img,distance((T)isovalue));
  2423           } else error("Compute distance function of image%s : Invalid argument '%s' "
  2424                        "(should be 'value[%%]').",gmic_inds,argument_text);
  2425           ++position; continue;
  2428         // Apply Hamilton-Jacobi PDE to compute distance to 0.
  2429         if (!cimg::strcmp("-hamilton",item0)) {
  2430           int nb_iter = 0; float band_size = 0; char end = 0;
  2431           if (std::sscanf(argument,"%d%c",&nb_iter,&end)==1 ||
  2432               std::sscanf(argument,"%d%*c%f%c",&nb_iter,&band_size,&end)==2) {
  2433             print("Apply %d iterations of Hamilton-Jacobi PDE on image%s.",nb_iter,gmic_inds);
  2434             cimg_foroff(indices,l) gmic_apply(images[indices[l]],distance_hamilton((unsigned int)nb_iter,band_size));
  2435           } else error("Apply %d iterations of Hamilton-Jacobi PDE on image%s : Invalid argument '%s' "
  2436                        "(should be 'nb_iter[,band_size]', with band_size>0).",nb_iter,gmic_inds,argument_text);
  2437           ++position; continue;
  2440         // Label regions.
  2441         gmic_simple_item("-label",label_regions,"Label regions on image%s.");
  2443         // Displacement field.
  2444         if (!cimg::strcmp("-displacement",item0)) {
  2445           float smooth = 0.1f, precision = 0.1f; int ind0 = no_ind, nbscales = 0, itermax = 1000; char sep = 0, end = 0;
  2446           if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2447               std::sscanf(argument,"[%d]%*c%f%c",&ind0,&smooth,&end)==2 ||
  2448               std::sscanf(argument,"[%d]%*c%f%*c%f%c",&ind0,&smooth,&precision,&end)==3 ||
  2449               std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%c",&ind0,&smooth,&precision,&nbscales,&end)==4 ||
  2450               std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%*c%d%c",&ind0,&smooth,&precision,&nbscales,&itermax,&end)==5) {
  2451             gmic_check_indice(ind0,"Compute displacement field of image%s");
  2452             print("Compute displacement field of image%s with target [%u] and smoothness %g.",
  2453                   gmic_inds,ind0,smooth);
  2454             const CImg<T> target = images[ind0];
  2455             cimg_foroff(indices,l) gmic_apply(images[indices[l]],displacement_field(target,smooth,precision,nbscales,itermax));
  2456           } else error("Compute displacement field of image%s : Invalid argument '%s' "
  2457                        "(should be '[indice][,smoothness[,precision[,nbscales[,itermax]]]]').",gmic_inds,argument_text);
  2458           ++position; continue;
  2461         // Sort.
  2462         gmic_simple_item("-sort",sort,"Sort values in image%s.");
  2464         // PSNR.
  2465         if (!cimg::strcmp("-psnr",item0)) {
  2466           double valmax = 255; char end = 0;
  2467           if (std::sscanf(argument,"%lf%c",&valmax,&end)==1) ++position;
  2468           if (images.size) {
  2469             const unsigned int siz = indices.size();
  2470             print("Compute a %ux%u matrix [%u] of PSNR values (max. pixel value is %g).",siz,siz,images.size,valmax);
  2471             CImg<T> res(siz,siz,1,1,(T)-1);
  2472             cimg_forXY(res,x,y) if (x>y) res(x,y) = res(y,x) = (T)images[indices[x]].PSNR(images[indices[y]],(float)valmax);
  2473             images.insert(res);
  2474             filenames.insert(CImg<char>("PSNR",5,1,1,1,false));
  2475           } else error("Compute PSNR : image list is empty.");
  2476           continue;
  2479         // Draw point.
  2480         if (!cimg::strcmp("-point",item0)) {
  2481           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
  2482           char sepx0 = 0, sepy0 = 0, sepz0 = 0, end = 0;
  2483           float x0 = 0, y0 = 0, z0 = 0, opacity = 1;
  2484           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,+-]",
  2485                           arg0,arg1,arg2,&opacity,color)>=2 &&
  2486               ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  2487                std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  2488               ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  2489                std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
  2490               ((std::sscanf(arg2,"%f%c%c",&z0,&sepz0,&end)==2 && sepz0=='%') ||
  2491                std::sscanf(arg2,"%f%c",&z0,&end)==1 || !arg2[0])) {
  2492             print("Draw point (%g%s,%g%s,%g%s) with color '%s' and opacity %g on image%s.",
  2493                   x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",z0,sepz0=='%'?"%":"",
  2494                   color[0]?color:"default",opacity,gmic_inds);
  2495             cimg_foroff(indices,l) {
  2496               CImg<T> &img = images[indices[l]];
  2497               CImg<T> col(img.dimv(),1,1,1,0);
  2498               col.fill(color,true);
  2499               const int
  2500                 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  2501                 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
  2502                 nz0 = (int)cimg::round(sepz0=='%'?z0*(img.dimz()-1)/100:z0,1);
  2503               gmic_apply(img,draw_point(nx0,ny0,nz0,col,opacity));
  2505           } else error("Draw point on image%s : Invalid argument '%s' "
  2506                        "(should be 'x[%%],y[%%][,z[%%][,opacity[,color]]])",gmic_inds,argument_text);
  2507           ++position; continue;
  2510         // Draw line.
  2511         if (!cimg::strcmp("-line",item0)) {
  2512           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, arg3[4096] = { 0 }, color[4096] = { 0 };
  2513           char sepx0 = 0, sepy0 = 0, sepx1 = 0, sepy1 = 0, end = 0;
  2514           float x0 = 0, y0 = 0, x1 = 0, y1 = 0, opacity = 1;
  2515           if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]"
  2516                           "%*c%f%*c%4095[0-9.eE,+-]",
  2517                           arg0,arg1,arg2,arg3,&opacity,color)>=4 &&
  2518               ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  2519                std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  2520               ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  2521                std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
  2522               ((std::sscanf(arg2,"%f%c%c",&x1,&sepx1,&end)==2 && sepx1=='%') ||
  2523                std::sscanf(arg2,"%f%c",&x1,&end)==1) &&
  2524               ((std::sscanf(arg3,"%f%c%c",&y1,&sepy1,&end)==2 && sepy1=='%') ||
  2525                std::sscanf(arg3,"%f%c",&y1,&end)==1)) {
  2526             print("Draw line (%g%s,%g%s) - (%g%s,%g%s) with color '%s' and opacity %g on image%s.",
  2527                   x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",x1,sepx1=='%'?"%":"",y1,sepy1=='%'?"%":"",
  2528                   color[0]?color:"default",opacity,gmic_inds);
  2529             cimg_foroff(indices,l) {
  2530               CImg<T> &img = images[indices[l]];
  2531               CImg<T> col(img.dimv(),1,1,1,0);
  2532               col.fill(color,true);
  2533               const int
  2534                 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  2535                 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
  2536                 nx1 = (int)cimg::round(sepx1=='%'?x1*(img.dimx()-1)/100:x1,1),
  2537                 ny1 = (int)cimg::round(sepy1=='%'?y1*(img.dimy()-1)/100:y1,1);
  2538               gmic_apply(img,draw_line(nx0,ny0,nx1,ny1,col,opacity));
  2540           } else error("Draw line on image%s : Invalid argument '%s' "
  2541                        "(should be 'x0[%%],y0[%%],x1[%%],y1[%%][,opacity[,color]]')",gmic_inds,argument_text);
  2542           ++position; continue;
  2545         // Draw polygon.
  2546         if (!cimg::strcmp("-polygon",item0)) {
  2547           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, tmp[4096] = { 0 }, sepx0 = 0, sepy0 = 0, end = 0;
  2548           int N = 0; float x0 = 0, y0 = 0, opacity = 1;
  2549           if (std::sscanf(argument,"%d%c",&N,&end)==2 && N>2) {
  2550             const char
  2551               *nargument = argument + std::sprintf(tmp,"%d",N) + 1,
  2552               *const eargument = argument + cimg::strlen(argument);
  2553             CImg<float> coords0(N,2,1,1,0);
  2554             CImg<bool> percents(N,2,1,1,0);
  2555             for (int n = 0; n<N; ++n) if (nargument<eargument) {
  2556               if (std::sscanf(nargument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]",arg0,arg1)==2 &&
  2557                   ((std::sscanf(arg0,"%f%c%c",&x0,&(sepx0=0),&end)==2 && sepx0=='%') ||
  2558                    std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  2559                   ((std::sscanf(arg1,"%f%c%c",&y0,&(sepy0=0),&end)==2 && sepy0=='%') ||
  2560                    std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
  2561                 coords0(n,0) = x0; percents(n,0) = (sepx0=='%');
  2562                 coords0(n,1) = y0; percents(n,1) = (sepy0=='%');
  2563                 nargument+=cimg::strlen(arg0) + cimg::strlen(arg1) + 2;
  2564               } else error("Draw polygon on image%s : Invalid or incomplete argument '%s' "
  2565                            "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
  2566                            gmic_inds,argument_text);
  2567             } else error("Draw polygon on image%s : Incomplete argument '%s' "
  2568                          "(%d xy-coordinates should be defined)",
  2569                          gmic_inds,argument_text,N);
  2570             if (nargument<eargument && std::sscanf(nargument,"%4095[0-9.eE+-]",arg0)==1 &&
  2571                 std::sscanf(arg0,"%f",&opacity)==1) nargument+=cimg::strlen(arg0)+1;
  2572             const char *const color = nargument<eargument?nargument:&(end=0);
  2573             print("Draw %d-vertices polygon with color '%s' and opacity %g on image%s.",
  2574                   N,color[0]?color:"default",opacity,gmic_inds);
  2575             cimg_foroff(indices,l) {
  2576               CImg<T> &img = images[indices[l]];
  2577               CImg<int> coords(coords0);
  2578               cimg_forX(coords,p) {
  2579                 if (percents(p,0)) coords(p,0) = (int)cimg::round(coords0(p,0)*(img.dimx()-1)/100,1);
  2580                 if (percents(p,1)) coords(p,1) = (int)cimg::round(coords0(p,1)*(img.dimy()-1)/100,1);
  2582               CImg<T> col(img.dimv(),1,1,1,0);
  2583               col.fill(color,true);
  2584               gmic_apply(img,draw_polygon(coords,col,opacity));
  2586           } else error("Draw polygon on image%s : Invalid argument '%s' "
  2587                        "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
  2588                        gmic_inds,argument_text);
  2589           ++position; continue;
  2592         // Draw ellipse.
  2593         if (!cimg::strcmp("-ellipse",item0)) {
  2594           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 };
  2595           char sepx0 = 0, sepy0 = 0, end = 0;
  2596           float x0 = 0, y0 = 0, r0 = 0, r1 = 0, ru = 1, rv = 0, opacity = 1;
  2597           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,+-]",
  2598                           arg0,arg1,&r0,&r1,&ru,&rv,&opacity,color)>=4 &&
  2599               ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  2600                std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
  2601               ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  2602                std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
  2603             print("Draw ellipse centered at (%g%s,%g%s) with radii (%g,%g), orientation (%g,%g), color '%s' "
  2604                   "and opacity %g on image%s.",
  2605                   x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",
  2606                   r0,r1,ru,rv,color[0]?color:"default",opacity,gmic_inds);
  2607             cimg_foroff(indices,l) {
  2608               CImg<T> &img = images[indices[l]];
  2609               CImg<T> col(img.dimv(),1,1,1,0);
  2610               col.fill(color,true);
  2611               const int
  2612                 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  2613                 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
  2614               gmic_apply(img,draw_ellipse(nx0,ny0,r0,r1,ru,rv,col,opacity));
  2616           } else error("Draw ellipse on image%s : Invalid argument '%s' "
  2617                        "(should be 'x[%%],y[%%],r,R[,u,v[,opacity[,color]]])",
  2618                        gmic_inds,argument_text);
  2619           ++position; continue;
  2622         // Draw text.
  2623         if (!cimg::strcmp("-text",item0)) {
  2624           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 }, text[4096] = { 0 };
  2625           char sepx0 = 0, sepy0 = 0, end = 0;
  2626           float x0 = 0, y0 = 0, opacity = 1; int siz = 11;
  2627           if (std::sscanf(argument,"%4095[^,],%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%*c%f%*c%4095[0-9.eE,+-]",
  2628                           text,arg0,arg1,&siz,&opacity,color)>=1 &&
  2629               ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
  2630                std::sscanf(arg0,"%f%c",&x0,&end)==1 || !arg0[0]) &&
  2631               ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
  2632                std::sscanf(arg1,"%f%c",&y0,&end)==1 || !arg1[0])) {
  2633             cimg::strclean(text); cimg::strescape(text);
  2634             print("Draw text \"%s\" at position (%g%s,%g%s) with font size %d, color '%s' "
  2635                   "and opacity %f on image%s.",
  2636                   text,x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",siz,color[0]?color:"default",opacity,gmic_inds);
  2637             cimg_foroff(indices,l) {
  2638               CImg<T> &img = images[indices[l]];
  2639               CImg<T> col(img.dimv(),1,1,1,0);
  2640               col.fill(color,true);
  2641               const int
  2642                 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
  2643                 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
  2644               gmic_apply(img,draw_text(nx0,ny0,text,col.ptr(),0,opacity,siz));
  2646           } else error("Draw text on image%s : Invalid argument '%s' "
  2647                        "(should be 'text[,x[%%],y[%%][,size[,opacity[,color]]]]').",
  2648                        gmic_inds,argument_text);
  2649           ++position; continue;
  2652         // Draw image.
  2653         if (!cimg::strcmp("-image",item0)) {
  2654           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, sepz = 0, end = 0;
  2655           int ind0 = no_ind, indm0 = no_ind; float x = 0, y = 0, z = 0, opacity = 1;
  2656           if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==1 && sep==']') ||
  2657                std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
  2658                std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
  2659                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 ||
  2660                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 ||
  2661                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",
  2662                             &ind0,arg0,arg1,arg2,&opacity,&indm0,&sep,&end)==7) &&
  2663               (!*arg0 ||
  2664                std::sscanf(arg0,"%f%c",&x,&end)==1 ||
  2665                (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
  2666               (!*arg1 ||
  2667                std::sscanf(arg1,"%f%c",&y,&end)==1 ||
  2668                (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%')) &&
  2669               (!*arg2 ||
  2670                std::sscanf(arg2,"%f%c",&z,&end)==1 ||
  2671                (std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%'))) {
  2672             gmic_check_indice(ind0,"Draw image on image%s");
  2673             const CImg<T> sprite = images[ind0];
  2674             CImg<T> mask;
  2675             if (indm0!=no_ind) {
  2676               gmic_check_indice(indm0,"Draw image on image%s");
  2677               mask = images[indm0];
  2678               print("Draw image [%d] at (%g%s,%g%s,%g%s), with mask [%d] and opacity %f on image%s.",
  2679                     ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",indm0,opacity,gmic_inds);
  2680             } else print("Draw image [%d] at (%g%s,%g%s,%g%s) with opacity %f on image%s.",
  2681                          ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",opacity,gmic_inds);
  2682             cimg_foroff(indices,l) {
  2683               CImg<T> &img = images[indices[l]];
  2684               const int
  2685                 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  2686                 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  2687                 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
  2688               if (indm0!=no_ind) { gmic_apply(img,draw_image(nx,ny,nz,sprite,mask,opacity)); }
  2689               else { gmic_apply(img,draw_image(nx,ny,nz,sprite,opacity)); }
  2691           } else error("Draw image on image%s : Invalid argument '%s' "
  2692                        "(should be '[indice][,x[%%][,y[%%][,z[%%][,opacity[,indice_mask]]]]]').",
  2693                        gmic_inds,argument_text);
  2694           ++position; continue;
  2697         // Draw 3D object.
  2698         if (!cimg::strcmp("-object3d",item0)) {
  2699           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, end = 0;
  2700           float x = 0, y = 0, z = 0, opacity = 1;
  2701           int ind0 = no_ind;
  2702           if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
  2703                std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
  2704                std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
  2705                std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,&z,&end)==4 ||
  2706                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) &&
  2707               (!*arg0 ||
  2708                std::sscanf(arg0,"%f%c",&x,&end)==1 ||
  2709                (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
  2710               (!*arg1 ||
  2711                std::sscanf(arg1,"%f%c",&y,&end)==1 ||
  2712                (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%'))) {
  2713             gmic_check_indice(ind0,"Draw 3D object on image%s");
  2714             if (!images[ind0].is_CImg3d())
  2715               error("Draw 3D object on image%s : Image [%d] is not a 3D object.",gmic_inds,ind0);
  2716             print("Draw 3D object [%d] at (%g%s,%g%s,%g) on image%s, with opacity %g.",
  2717                   ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,gmic_inds,opacity);
  2718             CImgList<unsigned int> primitives3d;
  2719             CImgList<unsigned char> colors3d;
  2720             CImg<float> opacities3d, points3d(images[ind0]);
  2721             points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  2722             opacities3d*=opacity;
  2723             cimg_foroff(indices,l) {
  2724               CImg<T> &img = images[indices[l]];
  2725               const float
  2726                 nx = (float)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  2727                 ny = (float)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1);
  2728               gmic_apply(img,draw_object3d(nx,ny,z,points3d,primitives3d,colors3d,opacities3d,
  2729                                            render3d,!is_oriented3d,focale3d,light3d_x,light3d_y,light3d_z,specular_light3d,
  2730                                            specular_shine3d,0));
  2732           } else error("Draw 3D object on image%s : Invalid argument '%s' "
  2733                        "(should be '[indice][,x[%%][,y[%%][,z[,opacity[,zoom[,u1,v1,w1,angle1[,...]]]]]]]').",
  2734                        gmic_inds,argument_text);
  2735           ++position; continue;
  2738         // Draw plasma fractal.
  2739         if (!cimg::strcmp("-plasma",item0)) {
  2740           float alpha = 1, beta = 1, opacity = 1; char end = 0;
  2741           if (std::sscanf(argument,"%f%c",&alpha,&end)==1 ||
  2742               std::sscanf(argument,"%f%*c%f%c",&alpha,&beta,&end)==2 ||
  2743               std::sscanf(argument,"%f%*c%f%*c%f%c",&alpha,&beta,&opacity,&end)==3) {
  2744             print("Draw plasma in image%s with alpha %g, beta %g and opacity %g.",gmic_inds,alpha,beta,opacity);
  2745             cimg_foroff(indices,l) gmic_apply(images[indices[l]],draw_plasma(alpha,beta,opacity));
  2746           } else error("Draw plasma in image%d : Invalid argument '%s' "
  2747                        "(should be 'alpha[,beta[,opacity]]').",gmic_inds,argument_text);
  2748           ++position; continue;
  2751         // Draw Mandelbrot/Julia fractal.
  2752         if (!cimg::strcmp("-mandelbrot",item0)) {
  2753           double z0r = -2, z0i = -2, z1r = 2, z1i = 2, paramr = 0, parami = 0; char end = 0;
  2754           float opacity = 1; int itermax = 100, julia = 0;
  2755           if (std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%c",&z0r,&z0i,&z1r,&z1i,&end)==4 ||
  2756               std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%c",&z0r,&z0i,&z1r,&z1i,&itermax,&end)==5 ||
  2757               std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%c",
  2758                           &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&end)==8 ||
  2759               std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%*c%f%c",
  2760                           &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&opacity,&end)==9) {
  2761             print("Draw %s fractal in image%s from complex area (%g,%g)-(%g,%g) with c0 = (%g,%g) (%d iterations).",
  2762                   julia?"Julia":"Mandelbrot",gmic_inds,z0r,z0i,z1r,z1i,paramr,parami,itermax);
  2763             cimg_foroff(indices,l)
  2764               gmic_apply(images[indices[l]],draw_mandelbrot(CImg<T>(),opacity,z0r,z0i,z1r,z1i,itermax,true,
  2765                                                             julia?true:false,paramr,parami));
  2766           } else error("Draw fractal in image%s : Invalid argument '%s' "
  2767                        "(should be 'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]').",gmic_inds,argument_text);
  2768           ++position; continue;
  2771         // Flood fill.
  2772         if (!cimg::strcmp("-flood",item0)) {
  2773           char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
  2774           char sepx = 0, sepy = 0, sepz = 0, end = 0;
  2775           float x = 0, y = 0, z = 0, tolerance = 0, opacity = 1;
  2776           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,+-]",
  2777                           arg0,arg1,arg2,&tolerance,&opacity,color)>=1 &&
  2778               ((std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
  2779                std::sscanf(arg0,"%f%c",&x,&end)==1) &&
  2780               ((std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
  2781                std::sscanf(arg1,"%f%c",&y,&end)==1 || !arg1[0]) &&
  2782               ((std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
  2783                std::sscanf(arg2,"%f%c",&z,&end)==1 || !arg2[0])) {
  2784             print("Flood fill image%s from (%g%s,%g%s,%g%s) with tolerance %g, opacity %g and color '%s'.",
  2785                   gmic_inds,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",tolerance,opacity,color);
  2786             cimg_foroff(indices,l) {
  2787               CImg<T> &img = images[indices[l]];
  2788               CImg<T> col(img.dimv(),1,1,1,0);
  2789               col.fill(color,true);
  2790               const int
  2791                 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
  2792                 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
  2793                 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
  2794               gmic_apply(img,draw_fill(nx,ny,nz,col,opacity,tolerance));
  2796           } else error("Flood fill image%s : Invalid argument '%s' "
  2797                        "(should be 'x[,y[,z[,tolerance[,opacity[,color]]]]]').",gmic_inds,argument_text);
  2798           ++position; continue;
  2801         //-------------------------
  2802         // Image list manipulation
  2803         //-------------------------
  2805         // Remove specified image(s).
  2806         if (!cimg::strcmp("-remove",item0) || !cimg::strcmp("-rm",item0)) {
  2807           print("Remove image%s",gmic_inds);
  2808           unsigned int off = 0;
  2809           cimg_foroff(indices,l) {
  2810             const unsigned int ind = indices[l] - off;
  2811             images.remove(ind); filenames.remove(ind);
  2812             ++off;
  2814           if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
  2815           continue;
  2818         // Keep specified image(s).
  2819         if (!cimg::strcmp("-keep",item0) || !cimg::strcmp("-k",item0)) {
  2820           print("Keep image%s",gmic_inds);
  2821           CImgList<T> nimages(indices.size());
  2822           cimg_foroff(indices,l) nimages[l].swap(images[indices[l]]);
  2823           nimages.transfer_to(images);
  2824           if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
  2825           continue;
  2828         // Move image(s) to specified position.
  2829         if (!cimg::strcmp("-move",item0) || !cimg::strcmp("-mv",item0)) {
  2830           int ind0 = no_ind; char end = 0;
  2831           if (std::sscanf(argument,"%d%c",&ind0,&end)==1) {
  2832             if (ind0<0) ind0+=images.size;
  2833             if (ind0<0) ind0 = 0;
  2834             if (ind0>(int)images.size) ind0 = images.size;
  2835             print("Move image%s to position %d.",gmic_inds,ind0);
  2836             CImgList<T> nimages;
  2837             CImgList<char> nfilenames;
  2838             cimg_foroff(indices,l) {
  2839               const unsigned int ind = indices[l];
  2840               nimages.insert(1); nimages.last().swap(images[ind]);
  2841               nfilenames.insert(1); nfilenames.last().swap(filenames[ind]);
  2843             images.insert(nimages,ind0); filenames.insert(nfilenames,ind0);
  2844             { cimglist_for(images,l) if (!images[l]) { images.remove(l); filenames.remove(l--); }}
  2845           } else error("Move image%s : Invalid argument '%s' "
  2846                        "(should be 'position').",gmic_inds,argument_text);
  2847           ++position; continue;
  2850         // Reverse images order.
  2851         if (!cimg::strcmp("-reverse",item0)) {
  2852           print("Reverse images order.");
  2853           CImgList<T> nimages(indices.size());
  2854           CImgList<char> nfilenames(indices.size());
  2855           cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }
  2856           nimages.reverse(); nfilenames.reverse();
  2857           { cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }}
  2858           continue;
  2861         // Set image name.
  2862         if (!cimg::strcmp("-name",item0)) {
  2863           cimg_foroff(indices,l) filenames[indices[l]].assign(argument,cimg::strlen(argument)+1,1,1,1,false);
  2864           ++position; continue;
  2867         //-------------------------
  2868         // 3D objects manipulation
  2869         //-------------------------
  2871         // Generate 3D cube.
  2872         if (!cimg::strcmp("-cube3d",item)) {
  2873           float size = 100; char end = 0;
  2874           if (std::sscanf(argument,"%f%c",&size,&end)==1) {
  2875             print("Generate 3D cube with size %g.",size);
  2876             CImgList<unsigned int> primitives3d;
  2877             CImg<float> points3d = CImg<T>::cube3d(primitives3d,size);
  2878             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2879             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2880             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2881             images.insert(points3d);
  2882             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2883           } else error("Generate 3D cube : Invalid argument '%s' "
  2884                        "(should be 'size').",argument_text);
  2885           ++position; continue;
  2888         // Generate 3D cone.
  2889         if (!cimg::strcmp("-cone3d",item)) {
  2890           float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
  2891           if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  2892               std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
  2893               std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
  2894             print("Generate 3D cone with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
  2895             CImgList<unsigned int> primitives3d;
  2896             CImg<float> points3d = CImg<T>::cone3d(primitives3d,radius,height,subdivisions);
  2897             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2898             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2899             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2900             images.insert(points3d);
  2901             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2902           } else error("Generate 3D cone : Invalid argument '%s' "
  2903                        "(should be 'radius[,height[,subdivisions]]').",argument_text);
  2904           ++position; continue;
  2907         // Generate 3D cylinder.
  2908         if (!cimg::strcmp("-cylinder3d",item)) {
  2909           float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
  2910           if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  2911               std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
  2912               std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
  2913             print("Generate 3D cylinder with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
  2914             CImgList<unsigned int> primitives3d;
  2915             CImg<float> points3d = CImg<T>::cylinder3d(primitives3d,radius,height,subdivisions);
  2916             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2917             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2918             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2919             images.insert(points3d);
  2920             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2921           } else error("Generate 3D cylinder : Invalid argument '%s' "
  2922                        "(should be 'radius[,height[,subdivisions]]').",argument_text);
  2923           ++position; continue;
  2926         // Generate 3D torus.
  2927         if (!cimg::strcmp("-torus3d",item)) {
  2928           float radius1 = 100, radius2 = 30; char end = 0; unsigned int subdivisions1 = 24, subdivisions2 = 12;
  2929           if (std::sscanf(argument,"%f%*c%f%c",&radius1,&radius2,&end)==2 ||
  2930               std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&radius1,&radius2,&subdivisions1,&subdivisions2,&end)==4) {
  2931             print("Generate 3D torus with radii %g and %g, and subdivisions %u and %u.",radius1,radius2,subdivisions1,subdivisions2);
  2932             CImgList<unsigned int> primitives3d;
  2933             CImg<float> points3d = CImg<T>::torus3d(primitives3d,radius1,radius2,subdivisions1,subdivisions2);
  2934             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2935             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2936             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2937             images.insert(points3d);
  2938             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2939           } else error("Generate 3D torus : Invalid argument '%s' "
  2940                        "(should be 'radius1,radius2[,subdivisions1,subdivisions2]').",argument_text);
  2941           ++position; continue;
  2944         // Generate 3D plane.
  2945         if (!cimg::strcmp("-plane3d",item)) {
  2946           float sizex = 100, sizey = 30; char end = 0; unsigned int subdivisionsx = 24, subdivisionsy = 12;
  2947           if (std::sscanf(argument,"%f%*c%f%c",&sizex,&sizey,&end)==2 ||
  2948               std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&sizex,&sizey,&subdivisionsx,&subdivisionsy,&end)==4) {
  2949             print("Generate 3D plane with dimensions %g and %g, and subdivisions %u and %u.",sizex,sizey,subdivisionsx,subdivisionsy);
  2950             CImgList<unsigned int> primitives3d;
  2951             CImg<float> points3d = CImg<T>::plane3d(primitives3d,sizex,sizey,subdivisionsx,subdivisionsy);
  2952             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2953             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2954             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2955             images.insert(points3d);
  2956             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2957           } else error("Generate 3D plane : Invalid argument '%s' "
  2958                        "(should be 'sizex,sizey[,subdivisionsx,subdivisionsy]').",argument_text);
  2959           ++position; continue;
  2962         // Generate 3D sphere.
  2963         if (!cimg::strcmp("-sphere3d",item)) {
  2964           float radius = 100; char end = 0; unsigned int subdivisions = 3;
  2965           if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
  2966               std::sscanf(argument,"%f%*c%u%c",&radius,&subdivisions,&end)==2) {
  2967             print("Generate 3D sphere with radius %g and %u subdivisions.",radius,subdivisions);
  2968             CImgList<unsigned int> primitives3d;
  2969             CImg<float> points3d = CImg<T>::sphere3d(primitives3d,radius,subdivisions);
  2970             CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
  2971             CImg<float> opacities3d(1,primitives3d.size,1,1,1);
  2972             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  2973             images.insert(points3d);
  2974             filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
  2975           } else error("Generate 3D sphere : Invalid argument '%s' "
  2976                        "(should be 'radius[,subdivisions]').",argument_text);
  2977           ++position; continue;
  2980         // Build 3D elevation.
  2981         if (!cimg::strcmp("-elevation3d",item0)) {
  2982           float zfact = 0.2f; char end = 0, sep = 0; int ind0 = no_ind;
  2983           if (std::sscanf(argument,"%f%c",&zfact,&end)==1 ||
  2984               (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')) {
  2985             CImg<typename CImg<T>::Tfloat> elev;
  2986             if (ind0!=no_ind) {
  2987               gmic_check_indice(ind0,"Build 3D elevation of image%s");
  2988               print("Build 3D elevation of image%s with elevation map [%d].",gmic_inds,ind0);
  2989               if (images[ind0].dimv()==1) elev = images[ind0];
  2990               else elev = images[ind0].get_pointwise_norm();
  2991             } else print("Build 3D elevation of image%s with z-factor %g.",gmic_inds,zfact);
  2992             cimg_foroff(indices,l) {
  2993               CImg<T>& img = images[indices[l]];
  2994               CImgList<unsigned int> primitives3d;
  2995               CImgList<unsigned char> colors3d;
  2996               CImg<float> opacities3d, points3d;
  2997               if (elev) points3d = img.get_elevation3d(primitives3d,colors3d,elev);
  2998               else {
  2999                 if (img.dimv()==1) (elev = img)*=zfact; else (elev = img.get_pointwise_norm())*=zfact;
  3000                 points3d = img.get_elevation3d(primitives3d,colors3d,elev);
  3001                 elev.assign();
  3003               opacities3d.assign(1,primitives3d.size,1,1,1);
  3004               points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3005               gmic_apply(img,replace(points3d));
  3007           } else error("Build 3D elevation : invalid argument '%s' "
  3008                        "(should be 'z-factor' or '[indice]').",argument_text);
  3009           ++position; continue;
  3012         // Build 3D isovalue.
  3013         if (!cimg::strcmp("-isovalue3d",item0)) {
  3014           float value = 0; char end = 0;
  3015           if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  3016             print("Build 3D isovalue %g of image%s.",value,gmic_inds);
  3017             cimg_foroff(indices,l) {
  3018               const unsigned int ind = indices[l];
  3019               CImg<T>& img = images[ind];
  3020               CImg<float> points3d;
  3021               CImgList<unsigned int> primitives3d;
  3022               CImgList<unsigned char> colors3d;
  3023               CImg<float> opacities3d;
  3024               CImg<unsigned char> palette;
  3025               palette.assign(3,img.dim,1,1,220).noise(35,1);
  3026               if (img.dim==1) palette(0) = palette(1) = palette(2) = 255;
  3027               else {
  3028                 palette(0,0) = 255; palette(1,0) = 30; palette(2,0) = 30;
  3029                 palette(0,1) = 30; palette(1,1) = 255; palette(2,1) = 30;
  3030                 if (img.dim>=3) palette(0,2) = 30; palette(1,2) = 30; palette(2,2) = 255;
  3032               cimg_forV(img,k) {
  3033                 CImgList<unsigned int> prims;
  3034                 const CImg<float> pts = img.get_shared_channel(k).get_isovalue3d(prims,value);
  3035                 if (pts) {
  3036                   points3d.append_object3d(primitives3d,pts,prims);
  3037                   colors3d.insert(prims.size,
  3038                                   CImg<unsigned char>::vector(palette(0,k),palette(1,k),palette(2,k)));
  3041               opacities3d.assign(1,primitives3d.size,1,1,1);
  3042               if (!points3d)
  3043                 warning("Build 3D isovalue of image [%u] : Isovalue %g not found.",ind,value);
  3044               else points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3045               gmic_apply(img,replace(points3d));
  3047           } else error("Build 3D isovalue of image%s : Invalid argument '%s' "
  3048                        "(should be 'isovalue').",gmic_inds,argument_text);
  3049           ++position; continue;
  3052         // Center a 3D object.
  3053         if (!cimg::strcmp("-center3d",item0) || !cimg::strcmp("-c3d",item0)) {
  3054           print("Center 3D object%s.",gmic_inds);
  3055           cimg_foroff(indices,l) {
  3056             const unsigned int ind = indices[l];
  3057             if (!images[ind].is_CImg3d())
  3058               error("Center 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3059             gmic_apply(images[ind],centerCImg3d());
  3061           continue;
  3064         // Normalize a 3D object.
  3065         if (!cimg::strcmp("-normalize3d",item0) || !cimg::strcmp("-n3d",item0)) {
  3066           print("Normalize 3D object%s.",gmic_inds);
  3067           cimg_foroff(indices,l) {
  3068             const unsigned int ind = indices[l];
  3069             if (!images[ind].is_CImg3d())
  3070               error("Normalize 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3071             gmic_apply(images[ind],normalizeCImg3d());
  3073           continue;
  3076         // Rotate a 3D object.
  3077         if (!cimg::strcmp("-rotate3d",item0) || !cimg::strcmp("-rot3d",item0)) {
  3078           float u = 0, v = 0, w = 1, angle = 0; char end = 0;
  3079           if (std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&u,&v,&w,&angle,&end)==4) {
  3080             print("Rotate 3D object%s around axis (%g,%g,%g) with angle %g.",gmic_inds,u,v,w,angle);
  3081             const CImg<float> rot = CImg<float>::rotation_matrix(u,v,w,(float)(angle*cimg::valuePI/180));
  3082             cimg_foroff(indices,l) {
  3083               const unsigned int ind = indices[l];
  3084               if (!images[ind].is_CImg3d())
  3085                 error("Rotate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3086               gmic_apply(images[ind],rotateCImg3d(rot));
  3088           } else error("Rotate 3D object%s : Invalid argument '%s' "
  3089                        "(should be 'u,v,w,angle').",gmic_inds,argument_text);
  3090           ++position; continue;
  3093         // Add 3D objects together or translate a 3D object.
  3094         if (!cimg::strcmp("-add3d",item0) || !cimg::strcmp("-+3d",item0)) {
  3095           float tx = 0, ty = 0, tz = 0; int ind0 = no_ind; char sep = 0, end = 0;
  3096           if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
  3097               std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
  3098               std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
  3099             print("Translate 3D object%s with vector (%g,%g,%g).",gmic_inds,tx,ty,tz);
  3100             cimg_foroff(indices,l) {
  3101               const unsigned int ind = indices[l];
  3102               if (!images[ind].is_CImg3d())
  3103                 error("Translate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3104               gmic_apply(images[ind],translateCImg3d(tx,ty,tz));
  3106             ++position;
  3107           } else if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
  3108             gmic_check_indice(ind0,"Merge object with 3D object%s.");
  3109             const CImg<T> img0 = images[ind0];
  3110             if (!img0.is_CImg3d()) error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind0);
  3111             print("Merge object [%d] with 3D object%s.",ind0,gmic_inds);
  3112             cimg_foroff(indices,l) {
  3113               const unsigned int ind = indices[l];
  3114               const CImg<T> &img = images[ind];
  3115               if (!img.is_CImg3d())
  3116                 error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind);
  3117               gmic_apply(images[ind],appendCImg3d(img0));
  3119             ++position;
  3120           } else {
  3121             print("Merge 3D object%s together.",gmic_inds);
  3122             if (indices) {
  3123               const unsigned int ind0 = indices[0];
  3124               if (!images[ind0].is_CImg3d())
  3125                 error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind0);
  3126               for (unsigned int siz = indices.size(), off = 0, l = 1; l<siz; ++l) {
  3127                 const unsigned int ind = indices[l] - off;
  3128                 if (!images[ind].is_CImg3d())
  3129                   error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind);
  3130                 images[ind0].appendCImg3d(images[ind]);
  3131                 images.remove(ind); filenames.remove(ind);
  3132                 ++off;
  3136           continue;
  3139         // Translate 3D object by the opposite vector.
  3140         if (!cimg::strcmp("-sub3d",item0) || !cimg::strcmp("--3d",item0)) {
  3141           float tx = 0, ty = 0, tz = 0; char end = 0;
  3142           if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
  3143               std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
  3144               std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
  3145             print("Translate 3D object%s with vector -(%g,%g,%g).",gmic_inds,tx,ty,tz);
  3146             cimg_foroff(indices,l) {
  3147               CImg<T>& img = images[indices[l]];
  3148               CImgList<unsigned int> primitives3d;
  3149               CImgList<unsigned char> colors3d;
  3150               CImg<float> opacities3d;
  3151               CImg<T> points3d;
  3152               if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  3153               points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  3154               points3d.get_shared_line(0)-=tx;
  3155               points3d.get_shared_line(1)-=ty;
  3156               points3d.get_shared_line(2)-=tz;
  3157               points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3158               if (get_version) {
  3159                 images.insert(1); points3d.transfer_to(images.last());
  3160                 filenames.insert(filenames[indices[l]]);
  3161               } else points3d.transfer_to(images[indices[l]]);
  3163           } else error("Translate 3D object%s : Invalid argument '%s' "
  3164                        "(should be 'tx,ty,tz').",gmic_inds,argument_text);
  3165           ++position; continue;
  3168         // Scale a 3D object.
  3169         bool divide = false;
  3170         if (!cimg::strcmp("-mul3d",item0) || !cimg::strcmp("-*3d",item0) ||
  3171             ((divide=true)==true && (!cimg::strcmp("-div3d",item0) || !cimg::strcmp("-/3d",item0)))) {
  3172           float sx = 0, sy = 1, sz = 1; char end = 0;
  3173           if ((std::sscanf(argument,"%f%c",&sx,&end)==1 && (sy = sz = sx),1) ||
  3174               std::sscanf(argument,"%f%*c%f%c",&sx,&sy,&end)==2 ||
  3175               std::sscanf(argument,"%f%*c%f%*c%f%c",&sx,&sy,&sz,&end)==3) {
  3176             if (divide) print("Scale 3D object%s with factors (1/%g,1/%g,1/%g).",gmic_inds,sx,sy,sz);
  3177             else print("Scale 3D object%s with factors (%g,%g,%g).",gmic_inds,sx,sy,sz);
  3178             cimg_foroff(indices,l) {
  3179               CImg<T>& img = images[indices[l]];
  3180               CImgList<unsigned int> primitives3d;
  3181               CImgList<unsigned char> colors3d;
  3182               CImg<float> opacities3d;
  3183               CImg<T> points3d;
  3184               if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  3185               points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  3186               if (divide) {
  3187                 points3d.get_shared_line(0)/=sx;
  3188                 points3d.get_shared_line(1)/=sy;
  3189                 points3d.get_shared_line(2)/=sz;
  3190               } else {
  3191                 points3d.get_shared_line(0)*=sx;
  3192                 points3d.get_shared_line(1)*=sy;
  3193                 points3d.get_shared_line(2)*=sz;
  3195               points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3196               if (get_version) {
  3197                 images.insert(1); points3d.transfer_to(images.last());
  3198                 filenames.insert(filenames[indices[l]]);
  3199               } else points3d.transfer_to(images[indices[l]]);
  3201           } else error("Scale 3D object%s : Invalid argument '%s' "
  3202                        "(should be 'fact' or 'factx,facty[,factz]').",gmic_inds,argument_text);
  3203           ++position; continue;
  3206         // Set color of 3D object(s).
  3207         if (!cimg::strcmp("-color3d",item0) || !cimg::strcmp("-col3d",item0)) {
  3208           float R = 200, G = 200, B = 200, opacity = -1; char end = 0;
  3209           if (std::sscanf(argument,"%f%*c%f%*c%f%c",&R,&G,&B,&end)==3 ||
  3210               std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&R,&G,&B,&opacity,&end)==4) {
  3211             const bool set_opacity = (opacity>=0);
  3212             R = (float)cimg::round(R,1); G = (float)cimg::round(G,1); B = (float)cimg::round(B,1);
  3213             if (R<0) R = 0; if (R>255) R = 255;
  3214             if (G<0) G = 0; if (G>255) G = 255;
  3215             if (B<0) B = 0; if (B>255) B = 255;
  3216             if (set_opacity) print("Set colors of 3D object%s to (%g,%g,%g) and opacity to %g.",gmic_inds,R,G,B,opacity);
  3217             else print("Set color of 3D object%s to (%g,%g,%g).",gmic_inds,R,G,B);
  3218             cimg_foroff(indices,l) {
  3219               const unsigned int ind = indices[l];
  3220               if (!images[ind].is_CImg3d())
  3221                 error("Set color of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3222               gmic_apply(images[ind],coloropacityCImg3d(R,G,B,opacity,true,set_opacity));
  3224           } else error("Set color of 3D object%s : Invalid argument '%s' "
  3225                        "(should be 'R,G,B[,opacity]').",gmic_inds,argument_text);
  3226           ++position; continue;
  3229         // Set opacity of 3D object(s).
  3230         if (!cimg::strcmp("-opacity3d",item0) || !cimg::strcmp("-opac3d",item0)) {
  3231           float opacity = 1; char end = 0;
  3232           if (std::sscanf(argument,"%f%c",&opacity,&end)==1) {
  3233             print("Set opacity of 3D object%s to %g.",gmic_inds,opacity);
  3234             cimg_foroff(indices,l) {
  3235               const unsigned int ind = indices[l];
  3236               if (!images[ind].is_CImg3d())
  3237                 error("Set opacity of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
  3238               gmic_apply(images[ind],coloropacityCImg3d(0,0,0,opacity,false,true));
  3240           } else error("Set opacity of 3D object%s : Invalid argument '%s' "
  3241                        "(should be 'opacity').",gmic_inds,argument_text);
  3242           ++position; continue;
  3245         // Invert 3D orientation.
  3246         if (!cimg::strcmp("-invert3d",item0) || !cimg::strcmp("-i3d",item0)) {
  3247           print("Invert orientation of 3D object%s.",gmic_inds);
  3248           cimg_foroff(indices,l) {
  3249             CImg<T> &img = images[indices[l]];
  3250             CImgList<unsigned int> primitives3d;
  3251             CImgList<unsigned char> colors3d;
  3252             CImg<float> opacities3d;
  3253             CImg<T> points3d;
  3254             if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  3255             points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  3256             if (primitives3d) primitives3d.invert_object3d();
  3257             points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3258             if (get_version) {
  3259               images.insert(1); points3d.transfer_to(images.last());
  3260               filenames.insert(filenames[indices[l]]);
  3261             } else points3d.transfer_to(images[indices[l]]);
  3263           continue;
  3266         // Split 3D object(s) into 6 vector images {header,N,vertices,primitives,colors,opacities}
  3267         if (!cimg::strcmp("-split3d",item0) || !cimg::strcmp("-s3d",item0)) {
  3268           print("Split 3D object%s into its different characteristics.",gmic_inds);
  3269           unsigned int off = 0;
  3270           cimg_foroff(indices,l) {
  3271             const unsigned int ind = indices[l] + off;
  3272             CImg<T> &img = images[ind];
  3273             const CImg<char> filename = filenames[ind];
  3274             CImgList<unsigned int> primitives3d;
  3275             CImgList<unsigned char> colors3d;
  3276             CImg<float> opacities3d;
  3277             CImg<T> points3d;
  3278             if (get_version) points3d.assign(img); else img.transfer_to(points3d);
  3279             points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
  3280             CImgList<T> split;
  3281             split.insert(CImg<T>("CImg3d",1,6,1,1,false)+=0.5f);
  3282             split.insert(CImg<T>::vector((T)points3d.dimx(),(T)primitives3d.size));
  3283             split.insert(1); points3d.resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(split.last());
  3284             points3d.assign();
  3285             CImgList<T> _prims;
  3286             cimglist_for(primitives3d,p)
  3287               _prims.insert(CImg<T>::vector((T)primitives3d[p].size())).insert(primitives3d[p]).last().unroll('y');
  3288             primitives3d.assign();
  3289             split.insert(_prims.get_append('y')); _prims.assign();
  3290             split.insert(colors3d.get_append('x').transpose().unroll('y')); colors3d.assign();
  3291             split.insert(1); opacities3d.transfer_to(split.last());
  3292             if (get_version) {
  3293               images.insert(split);
  3294               filenames.insert(split.size,filename);
  3295             } else {
  3296               images.remove(ind); images.insert(split,ind);
  3297               filenames.remove(ind); filenames.insert(split.size,filename,ind);
  3298               off+=split.size-1;
  3301           continue;
  3304         // Set 3D light position.
  3305         if (!cimg::strcmp("-light3d",item) || !cimg::strcmp("-l3d",item)) {
  3306           float lx = 0, ly = 0, lz = -5000; char end = 0;
  3307           if (std::sscanf(argument,"%f%*c%f%*c%f%c",&lx,&ly,&lz,&end)==3) {
  3308             print("Set 3D light position at (%g,%g,%g).",lx,ly,lz);
  3309             light3d_x = lx;
  3310             light3d_y = ly;
  3311             light3d_z = lz;
  3312           } else error("Set 3D light position : Invalid argument '%s' "
  3313                        "(should be 'posx,posy,posz').",argument_text);
  3314           ++position; continue;
  3317         // Set 3D focale.
  3318         if (!cimg::strcmp("-focale3d",item) || !cimg::strcmp("-f3d",item)) {
  3319           float focale = 500; char end = 0;
  3320           if (std::sscanf(argument,"%f%c",&focale,&end)==1) {
  3321             focale3d = focale;
  3322             print("Set 3D focale to %g.",focale);
  3323           } else error("Set 3D focale : Invalid argument '%s' "
  3324                        "(should be 'value').");
  3325           ++position; continue;
  3328         // Set 3D specular light parameters.
  3329         if (!cimg::strcmp("-specl3d",item) || !cimg::strcmp("-sl3d",item)) {
  3330           float value = 0; char end = 0;
  3331           if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  3332             specular_light3d = value;
  3333             print("Set amount of 3D specular light to %g.",specular_light3d);
  3335           else error("Set amount of 3D specular light : invalid argument '%s'"
  3336                      "(should be 'value').",
  3337                      argument_text);
  3338           ++position; continue;
  3341         if (!cimg::strcmp("-specs3d",item) || !cimg::strcmp("-ss3d",item)) {
  3342           float value = 0; char end = 0;
  3343           if (std::sscanf(argument,"%f%c",&value,&end)==1) {
  3344             specular_shine3d = value;
  3345             print("Set shininess of 3D specular light to %g.",specular_shine3d);
  3347           else error("Set shininess of 3D specular light : invalid argument '%s'"
  3348                      "(should be 'value').",
  3349                      argument_text);
  3350           ++position; continue;
  3353         // Switch double-sided mode for 3D rendering.
  3354         if (!cimg::strcmp("-orient3d",item) || !cimg::strcmp("-o3d",item)) {
  3355           is_oriented3d = !is_oriented3d;
  3356           continue;
  3359         // Set 3D rendering mode.
  3360         if (!cimg::strcmp("-render3d",item) || !cimg::strcmp("-r3d",item)) {
  3361           unsigned int value = 0; char end = 0;
  3362           if (std::sscanf(argument,"%u%c",&value,&end)==1) {
  3363             render3d = value;
  3364             print("Set static 3D render mode to %s.",
  3365                   render3d==-1?"bounding-box":
  3366                   render3d==0?"pointwise":render3d==1?"linear":render3d==2?"flat":
  3367                   render3d==3?"flat-shaded":render3d==4?"Gouraud-shaded":
  3368                   render3d==5?"Phong-shaded":"none");
  3370           else error("Set static 3D render mode : invalid argument '%s'"
  3371                      "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
  3372                      argument_text);
  3373           ++position; continue;
  3376         if (!cimg::strcmp("-renderd3d",item) || !cimg::strcmp("-rd3d",item)) {
  3377           unsigned int value = 0; char end = 0;
  3378           if (std::sscanf(argument,"%u%c",&value,&end)==1) {
  3379             renderd3d = value;
  3380             print("Set dynamic 3D render mode to %s.",
  3381                   renderd3d==-1?"bounding-box":
  3382                   renderd3d==0?"pointwise":renderd3d==1?"linear":renderd3d==2?"flat":
  3383                   renderd3d==3?"flat-shaded":renderd3d==4?"Gouraud-shaded":
  3384                   renderd3d==5?"Phong-shaded":"none");
  3386           else error("Set dynamic 3D render mode : invalid argument '%s'"
  3387                      "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
  3388                      argument_text);
  3389           ++position; continue;
  3392         // Set 3D background color.
  3393         if (!cimg::strcmp("-background3d",item) || !cimg::strcmp("-b3d",item)) {
  3394           int R = 0, G = 0, B = 0; char end = 0;
  3395           const int nb = std::sscanf(argument,"%d%*c%d%*c%d%c",&R,&G,&B,&end);
  3396           switch (nb) {
  3397           case 1 : background3d[0] = background3d[1] = background3d[2] = R; break;
  3398           case 2 : background3d[0] = R; background3d[1] = background3d[2] = G; break;
  3399           case 3 : background3d[0] = R; background3d[1] = G; background3d[2] = B; break;
  3400           default: error("Set 3D background color : Invalid argument '%s'.",argument_text);
  3402           print("Set 3D background color to (%d,%d,%d).",
  3403                 (int)background3d[0],(int)background3d[1],(int)background3d[2]);
  3404           ++position; continue;
  3407         //----------------
  3408         // Other commands.
  3409         //----------------
  3411         // No operations : do nothing
  3412         if (!cimg::strcmp("-nop",item)) {
  3413           continue;
  3416         // Skip next argument;
  3417         if (!cimg::strcmp("-skip",item)) {
  3418           ++position;
  3419           continue;
  3422         // Echo.
  3423         if (!cimg::strcmp("-echo",item) || !cimg::strcmp("-e",item)) {
  3424           const int l = cimg::strlen(argument);
  3425           if (l>=2 && argument[0]=='"' && argument[l-1]=='"') {
  3426             if (l==2) print(""); else {
  3427               CImg<char> nargument(argument+1,l-1,1,1,1,false);
  3428               nargument(l-2)=0;
  3429               print("%s",nargument.ptr());
  3431           } else print("%s",argument);
  3432           ++position; continue;
  3435         // Print.
  3436         if (!cimg::strcmp("-print",item0) || !cimg::strcmp("-p",item0)) {
  3437           if (images.size) {
  3438             print("Print image%s.\n\n",gmic_inds);
  3439             char title[4096];
  3440             if (verbosity_level>=0) cimg_foroff(indices,l) {
  3441               const unsigned int ind = indices[l];
  3442               std::sprintf(title,"image [%u] = '%s'",ind,filenames[ind].ptr());
  3443               images[ind].print(title);
  3445             is_released = true;
  3446           } else print("Print image[].");
  3447           continue;
  3450         // Quit.
  3451         if (!cimg::strcmp("-quit",item) || !cimg::strcmp("-q",item)) {
  3452           print("Quit.");
  3453           is_released = true;
  3454           dowhile.assign();
  3455           repeatdone.assign();
  3456           position = command_line.size;
  3457           continue;
  3460         // Do...while.
  3461         if (!cimg::strcmp("-do",item)) {
  3462           dowhile.insert(CImg<int>::vector((int)position));
  3463           continue;
  3466         if (!cimg::strcmp("-while",item)) {
  3467           double cond = 0; char end = 0;
  3468           if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
  3469           if (!dowhile) error("Directive '-while' is not associated with a '-do' command.");
  3470           if (cond<=0) dowhile.remove();
  3471           else { position = (unsigned int)dowhile.last()(0); continue; }
  3472           ++position; continue;
  3475         // If..else..endif
  3476         if (!cimg::strcmp("-if",item)) {
  3477           double cond = 0; char end = 0;
  3478           if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
  3479           if (cond<=0) {
  3480             for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
  3481               const char *it = command_line[position].ptr();
  3482               if (!cimg::strcmp("-if",it)) ++nbifs;
  3483               if (!cimg::strcmp("-endif",it)) --nbifs;
  3484               if (!cimg::strcmp("-else",it) && nbifs==1) --nbifs;
  3486             continue;
  3488           ++position; continue;
  3490         if (!cimg::strcmp("-else",item)) {
  3491           for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
  3492             if (!cimg::strcmp("-if",command_line[position].ptr())) ++nbifs;
  3493             if (!cimg::strcmp("-endif",command_line[position].ptr())) --nbifs;
  3495           continue;
  3497         if (!cimg::strcmp("-endif",item)) continue;
  3499         // Repeat...done
  3500         if (!cimg::strcmp("-repeat",item)) {
  3501           float fnb = 0; char end = 0;
  3502           if (std::sscanf(argument,"%f%c",&fnb,&end)==1) {
  3503             const int nb = (int)fnb;
  3504             if (nb>0) repeatdone.insert(CImg<int>::vector((int)position+1,nb));
  3505             else {
  3506               int nbrepeats = 0;
  3507               for (nbrepeats = 1; nbrepeats && position<command_line.size; ++position) {
  3508                 const char *it = command_line[position].ptr();
  3509                 if (!cimg::strcmp("-repeat",it)) ++nbrepeats;
  3510                 if (!cimg::strcmp("-done",it)) --nbrepeats;
  3512               if (nbrepeats && position>=command_line.size)
  3513                 error("Directive '-done' is missing after a '-repeat' command.");
  3514               continue;
  3516           } else error("Repeat operation : Invalid argument '%s' "
  3517                        "(should be a number).",argument_text);
  3518           ++position; continue;
  3521         if (!cimg::strcmp("-done",item)) {
  3522           if (!repeatdone) error("Directive '-done' is not associated with a '-repeat' command.");
  3523           if (--repeatdone.last()(1))
  3524             position = (unsigned int)repeatdone.last()(0);
  3525           else repeatdone.remove();
  3526           continue;
  3529         // Check argument type
  3530         if (!cimg::strcmp("-int",item)) {
  3531           char it[4096], end = 0, sep = 0; int value = 0;
  3532           if (*argument) for (const char *nargument = argument; *nargument; ) {
  3533             const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
  3534             if (nb) {
  3535               if (std::sscanf(it,"%d%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
  3536               else error("Argument '%s' is not an integer value.",it);
  3537             } else error("Argument '%s' is not an integer value.",argument_text);
  3539           ++position; continue;
  3542         if (!cimg::strcmp("-float",item)) {
  3543           char it[4096], end = 0, sep = 0; double value = 0;
  3544           if (*argument) for (const char *nargument = argument; *nargument; ) {
  3545             const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
  3546             if (nb) {
  3547               if (std::sscanf(it,"%lf%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
  3548               else error("Argument '%s' is not a float value.",it);
  3549             } else error("Argument '%s' is not a float value.",argument_text);
  3551           ++position; continue;
  3554         //--------------------------
  3555         // Input/Output and Display
  3556         //--------------------------
  3558         // Display.
  3559         if (!cimg::strcmp("-display",item0) || !cimg::strcmp("-d",item0)) {
  3560           if (display_images(images,indices,true)) is_released = true;
  3561           continue;
  3564         // Display 3D object.
  3565         if (!cimg::strcmp("-display3d",item0) || !cimg::strcmp("-d3d",item0)) {
  3566           if (display_objects3d(images,indices,true)) is_released = true;
  3567           continue;
  3570         // Display as a graph plot.
  3571         if (!cimg::strcmp("-plot",item0)) {
  3572           int plot_type = 1, vertex_type = 1; double ymin = 0, ymax = 0, xmin = 0, xmax = 0; char end = 0;
  3573           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);
  3574           if (nb==1 || nb==2 || nb==4 || nb==6) ++position;
  3575           else { plot_type = 1; vertex_type = 0; ymin = ymax = xmin = xmax = 0; }
  3576           is_released |= display_plots(images,indices,plot_type,vertex_type,xmin,xmax,ymin,ymax,true);
  3577           continue;
  3580         // Select image feature.
  3581         if (!cimg::strcmp("-select",item0)) {
  3582           int select_type = 0; char end = 0;
  3583           if (std::sscanf(argument,"%d%c",&select_type,&end)==1) {
  3584             cimg_foroff(indices,l) gmic_apply(images[indices[l]],select(filenames[indices[l]].ptr(),select_type));
  3585           } else error("Select image%s : Invalid argument '%s' "
  3586                        "(should be 'select_type').",gmic_inds,argument_text);
  3587           ++position; continue;
  3590         // Output.
  3591         if (!cimg::strcmp("-output",item0) || !cimg::strcmp("-o",item0)) {
  3592           char filename[4096] = { 0 }; char options[4096] = { 0 };
  3593           if (std::sscanf(argument,"%4095[^,],%s",filename,options)!=2) std::strcpy(filename,argument);
  3594           const char *const ext = cimg::split_filename(filename);
  3595           if (!cimg::strcasecmp("off",ext)) {
  3596             char nfilename[4096] = { 0 };
  3597             std::strcpy(nfilename,filename);
  3598             const unsigned int siz = indices.size();
  3599             cimg_foroff(indices,l) {
  3600               const unsigned int ind = indices[l];
  3601               if (siz!=1) cimg::number_filename(filename,l,6,nfilename);
  3602               if (!images[ind].is_CImg3d())
  3603                 error("Output 3D object [%u] as file '%s' : Image [%u] is not a 3D object.",ind,nfilename,ind);
  3604               print("Output 3D object [%u] as file '%s'.",ind,nfilename);
  3605               CImgList<unsigned int> primitives3d;
  3606               CImgList<unsigned char> colors3d;
  3607               CImg<float> opacities3d;
  3608               CImg<float> points3d(images[ind]);
  3609               points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d).save_off(nfilename,primitives3d,colors3d);
  3611           } else if (!cimg::strcasecmp("jpeg",ext) || !cimg::strcasecmp("jpg",ext)) {
  3612             int quality = 100; char end = 0;
  3613             if (std::sscanf(options,"%d%c",&quality,&end)!=1) quality = 100;
  3614             if (quality<0) quality = 0; else if (quality>100) quality = 100;
  3615             CImgList<T> output_images;
  3616             cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
  3617             print("Output image%s as file '%s', with quality %u%%",gmic_inds,filename,quality);
  3618             if (!output_images) throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.",
  3619                                                             output_images.pixel_type(),filename,
  3620                                                             output_images.size,output_images.data);
  3621             if (output_images.size==1) output_images[0].save_jpeg(filename,quality);
  3622             else {
  3623               char nfilename[1024];
  3624               cimglist_for(output_images,l) {
  3625                 cimg::number_filename(filename,l,6,nfilename);
  3626                 output_images[l].save_jpeg(nfilename,quality);
  3629           } else {
  3630             CImgList<T> output_images;
  3631             cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
  3632             print("Output image%s as file '%s'.",gmic_inds,filename);
  3633             output_images.save(filename);
  3635           is_released = true; ++position; continue;
  3638         // Substitute macros commands if necessary.
  3639         if (cimg::strcmp("-i",item0) && cimg::strcmp("-input",item0)) {
  3640           bool macro_found = false;
  3641           cimglist_for(macros,l) {
  3642             const char
  3643               *const macro = macros[l].ptr(),
  3644               *const command = commands[l].ptr();
  3646             if (!cimg::strcmp(item+1,macro) && *command) {
  3647               CImgList<char> arguments(256);
  3648               unsigned int nb_arguments = 0;
  3649               char s_argument[4096] = { 0 }, tmp[4096] = { 0 }, tmp2[4096] = { 0 };
  3650               bool has_arguments = false;
  3651               macro_found = true;
  3652               debug("Found macro '%s', substituting by '%s'.",macro,command);
  3654               // Get command-line values of macro arguments.
  3655               if (argument)
  3656                 for (const char *nargument = argument; nb_arguments<256 && *nargument &&
  3657                        std::sscanf(nargument,"%4095[^,]",s_argument)==1;) {
  3658                   CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[nb_arguments++]);
  3659                   nargument+=cimg::strlen(s_argument);
  3660                   if (*nargument) ++nargument;
  3663               // Substitute arguments in macro command expression.
  3664               CImg<char> substituted_command;
  3665               CImgList<char> lreplacement;
  3666               for (const char *ncommand = command; *ncommand;) if (*ncommand=='$') {
  3667                 char *replace_text = 0, sep = 0;
  3668                 int ind = 0, ind1 = 0;
  3670                 // Replace $# and ${#}.
  3671                 if (ncommand[1]=='#' || (ncommand[1]=='{' && ncommand[2]=='#' && ncommand[3]=='}')) {
  3672                   std::sprintf(replace_text=s_argument,"%u",nb_arguments);
  3673                   ncommand+=(ncommand[1]=='#')?2:4;
  3674                   has_arguments = true;
  3676                   // Replace $* and ${*}.
  3677                 } else if (ncommand[1]=='*' || (ncommand[1]=='{' && ncommand[2]=='*' && ncommand[3]=='}')) {
  3678                   replace_text = &(s_argument[0]=0);
  3679                   for (unsigned int j = 1; j<=nb_arguments; ++j) {
  3680                     replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
  3681                     if (j<nb_arguments) *(replace_text++) = ',';
  3683                   replace_text = s_argument;
  3684                   ncommand+=(ncommand[1]=='*')?2:4;
  3685                   has_arguments = true;
  3687                   // Replace ${i*}.
  3688                 } else if (std::sscanf(ncommand,"${%d*%c",&ind,&sep)==2 &&
  3689                            ind>0 && ind<256 && sep=='}') {
  3690                   replace_text = &(s_argument[0]=0);
  3691                   for (unsigned int j = ind; j<=nb_arguments; ++j) {
  3692                     replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
  3693                     if (j<nb_arguments) *(replace_text++) = ',';
  3695                   replace_text = s_argument;
  3696                   ncommand+=std::sprintf(tmp,"${%d*}",ind);
  3697                   has_arguments = true;
  3699                   // Replace $i and ${i}.
  3700                 } else if ((std::sscanf(ncommand,"$%d",&ind)==1 ||
  3701                             (std::sscanf(ncommand,"${%d%c",&ind,&sep)==2 && sep=='}')) &&
  3702                            ind>0 && ind<256) {
  3703                   if (!arguments[ind-1]) {
  3704                     if (sep=='}') error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d}').",macro,ind,ind);
  3705                     else error("Macro '%s' : Argument '$%d' is undefined (in expression '$%d').",macro,ind,ind);
  3707                   replace_text = arguments[ind-1].ptr();
  3708                   ncommand+=std::sprintf(tmp,"$%d",ind) + (sep=='}'?2:0);
  3709                   has_arguments = true;
  3711                   // Replace ${i=$#}.
  3712                 } else if (std::sscanf(ncommand,"${%d=$#%c",&ind,&sep)==2 &&
  3713                            ind>0 && ind<256 && sep=='}') {
  3714                   std::sprintf(replace_text=s_argument,"%g",(double)nb_arguments);
  3715                   CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[ind-1]);
  3716                   ncommand+=std::sprintf(tmp,"${%d=$#}",ind);
  3717                   has_arguments = true;
  3719                   // Replace ${i=$j}.
  3720                 } else if (std::sscanf(ncommand,"${%d=$%d%c",&ind,&ind1,&sep)==3 && sep=='}' &&
  3721                            ind>0 && ind<256 && ind1>0 && ind1<256) {
  3722                   if (!arguments[ind1-1])
  3723                     error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d=$%d}').",macro,ind1,ind,ind1);
  3724                   if (!arguments[ind-1]) arguments[ind-1] = arguments[ind1-1];
  3725                   replace_text = arguments[ind-1].ptr();
  3726                   ncommand+=std::sprintf(tmp,"${%d=$%d}",ind,ind1);
  3727                   has_arguments = true;
  3729                   // Replace ${i=default}.
  3730                 } else if (std::sscanf(ncommand,"${%d=%4095[^}]%c",&ind,tmp,&sep)==3 && sep=='}' &&
  3731                            ind>0 && ind<256) {
  3732                   if (!arguments[ind-1]) CImg<char>(tmp,cimg::strlen(tmp)+1,1,1,1,false).transfer_to(arguments[ind-1]);
  3733                   replace_text = arguments[ind-1].ptr();
  3734                   ncommand+=cimg::strlen(tmp) + 4 + std::sprintf(tmp2,"%d",ind);
  3735                   has_arguments = true;
  3737                   // Any other expression starting by '$'.
  3738                 } else {
  3739                   replace_text = &(s_argument[0]='$');
  3740                   if (std::sscanf(ncommand,"%4095[^$]",s_argument+1)!=1) { s_argument[1] = 0; ++ncommand; }
  3741                   else ncommand+=cimg::strlen(s_argument);
  3744                 const int replace_length = cimg::strlen(replace_text);
  3745                 if (replace_length) {
  3746                   lreplacement.insert(1);
  3747                   CImg<char>(replace_text,replace_length,1,1,1,false).transfer_to(lreplacement.last());
  3750               } else {
  3751                 std::sscanf(ncommand,"%4095[^$]",s_argument);
  3752                 const int replace_length = cimg::strlen(s_argument);
  3753                 if (replace_length) {
  3754                   lreplacement.insert(1);
  3755                   CImg<char>(s_argument,replace_length,1,1,1,false).transfer_to(lreplacement.last());
  3756                   ncommand+=cimg::strlen(s_argument);
  3759               const CImg<char> zero(1,1,1,1,0);
  3760               lreplacement.insert(zero).get_append('x').transfer_to(substituted_command);
  3762               // Substitute macro expression in command line.
  3763               bool is_dquote = false;
  3764               cimg_foroff(substituted_command,k)
  3765                 if (substituted_command[k]=='"') is_dquote = !is_dquote;
  3766                 else if (is_dquote && substituted_command[k]==' ') substituted_command[k] = 30;
  3767               CImgList<char> command_items = substituted_command.get_split(' ',false,false);
  3768               cimglist_for(command_items,k) {
  3769                 CImg<char> &item = command_items[k];
  3770                 cimg_foroff(item,l) if (item[l]==30) item[l]=' ';
  3772               cimglist_for(command_items,k) command_items[k].append(zero,'y');
  3773               if (position<command_line.size && has_arguments) command_line.remove(position);
  3774               command_line.remove(--position);
  3775               command_line.insert(command_items,position);
  3776               break;
  3779           if (macro_found) continue;
  3783       // Input.
  3784       if (!cimg::strcmp("-i",item0) || !cimg::strcmp("-input",item0)) ++position;
  3785       else { if (get_version) --item; argument = item; item1[0] = 0; }
  3786       if (!cimg::strlen(item1)) indices.assign(1,1,1,1,images.size);
  3787       CImgList<T> input_images;
  3788       CImgList<char> input_filenames;
  3789       bool obj3d = false;
  3790       char st_inds[4096] = { 0 }, stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
  3791       char end = 0, sep = 0, sepx = 0, sepy = 0, sepz = 0, sepv = 0;
  3792       int nb = 1, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
  3793       float dx = 0, dy = 1, dz = 1, dv = 1;
  3795       if (std::sscanf(argument,"[%4095[0-9%,:-]]%*c%d%c",st_inds,&nb,&end)==2 ||
  3796           (std::sscanf(argument,"[%4095[0-9%,:-]%c%c",st_inds,&sep,&end)==2 && sep==']')) {
  3798         // nb copies of existing sub-images.
  3799         const CImg<unsigned int> indices0 = indices2cimg(st_inds,images.size,"-input");
  3800         char st_tmp[4096] = { 0 }; std::strcpy(st_tmp,indices2string(indices0,true));
  3801         if (nb<=0) error("Input %d copies of image%s : Invalid argument '%s'.",
  3802                          nb,st_tmp,argument_text);
  3803         if (nb!=1) print("Input %d copies of image%s at position%s",nb,st_tmp,gmic_inds);
  3804         else print("Input copy of image%s at position%s",st_tmp,gmic_inds);
  3805         for (int i = 0; i<nb; ++i) cimg_foroff(indices0,l) {
  3806           input_images.insert(images[indices0[l]]);
  3807           input_filenames.insert(filenames[indices0[l]]);
  3809       } 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",
  3810                                stx,sty,stz,stv,&(nb=1),&end)==5 ||
  3811                    std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
  3812                                stx,sty,stz,stv,&end)==4) &&
  3813                   (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  3814                    (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  3815                    (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  3816                   (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  3817                    (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  3818                    (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
  3819                   (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
  3820                    (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
  3821                    (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']')) &&
  3822                   (std::sscanf(stv,"%f%c",&dv,&end)==1 ||
  3823                    (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%') ||
  3824                    (std::sscanf(stv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']'))) ||
  3825                  (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
  3826                   (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  3827                    (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  3828                    (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  3829                   (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  3830                    (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  3831                    (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
  3832                   (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
  3833                    (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
  3834                    (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']'))) ||
  3835                  (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,&end)==2 &&
  3836                   (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  3837                    (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  3838                    (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
  3839                   (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
  3840                    (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
  3841                    (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']'))) ||
  3842                  (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",stx,&end)==1 &&
  3843                   (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
  3844                    (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
  3845                    (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')))) {
  3847         // nb new black image.
  3848         if (indx!=no_ind) { gmic_check_indice(indx,"Input black image%s"); dx = (float)images[indx].dimx(); sepx = 0; }
  3849         if (indy!=no_ind) { gmic_check_indice(indy,"Input black image%s"); dy = (float)images[indy].dimy(); sepy = 0; }
  3850         if (indz!=no_ind) { gmic_check_indice(indz,"Input black image%s"); dz = (float)images[indz].dimz(); sepz = 0; }
  3851         if (indv!=no_ind) { gmic_check_indice(indv,"Input black image%s"); dv = (float)images[indv].dimv(); sepv = 0; }
  3852         if (sepx=='%') { dx = images.size?dx*images.last().dimx()/100:0; if (!(int)dx) ++dx; }
  3853         if (sepy=='%') { dy = images.size?dy*images.last().dimy()/100:0; if (!(int)dy) ++dy; }
  3854         if (sepz=='%') { dz = images.size?dz*images.last().dimz()/100:0; if (!(int)dz) ++dz; }
  3855         if (sepv=='%') { dv = images.size?dv*images.last().dimv()/100:0; if (!(int)dv) ++dv; }
  3857         if (nb<=0) error("Input %d black image%s : Invalid number of copies.",nb,gmic_inds);
  3858         if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
  3859           error("Input %d black image%s : Invalid image dimensions %gx%gx%gx%g.",
  3860                 nb,gmic_inds,dx,dy,dz,dv);
  3861         if (nb!=1) print("Input %d black images at position%s",nb,gmic_inds);
  3862         else print("Input black image at position%s",gmic_inds);
  3863         CImg<T> empty((int)dx,(int)dy,(int)dz,(int)dv,0);
  3864         input_images.insert(nb-1,empty); input_images.insert(1);
  3865         input_images.last().swap(empty);
  3866         filenames.insert(input_images.size,CImg<char>("(gmic)",7,1,1,1,false));
  3867       } else if (std::sscanf(argument,"(%4095[^)])x%d%c",stx,&(nb=1),&end)==2 ||
  3868                  (std::sscanf(argument,"(%4095[^)]%c%c",stx,&sep,&end)==2 && sep==')')) {
  3870         // Insert nb IxJxKxL image(s) with specified values.
  3871         if (nb<=0) error("Input %d images : Invalid number of copies.",nb);
  3872         unsigned int cx = 0, cy = 0, cz = 0, cv = 0, maxcx = 0, maxcy = 0, maxcz = 0;
  3873         const char *nargument = 0;
  3874         for (nargument = argument+1; *nargument!=')'; ) {
  3875           char s_value[256] = { 0 }, separator = 0; double value = 0;
  3876           if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
  3877             if (cx>maxcx) maxcx = cx;
  3878             if (cy>maxcy) maxcy = cy;
  3879             if (cz>maxcz) maxcz = cz;
  3880             switch (separator) {
  3881             case '^' : cx = cy = cz = 0; ++cv; break;
  3882             case '/' : cx = cy = 0; ++cz; break;
  3883             case ';' : cx = 0; ++cy; break;
  3884             default : ++cx;
  3886             nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
  3887           } else break;
  3889         if (*nargument!=')') error("Input %d images : Invalid input string '%s'.",nb,argument);
  3891         CImg<T> img(maxcx+1,maxcy+1,maxcz+1,cv+1,0);
  3892         cx = cy = cz = cv = 0;
  3893         for (nargument = argument+1; *nargument; ) {
  3894           char s_value[256] = { 0 }, separator = 0; double value = 0;
  3895           if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
  3896             img(cx,cy,cz,cv) = (T)value;
  3897             switch (separator) {
  3898             case '^' : cx = cy = cz = 0; ++cv; break;
  3899             case '/' : cx = cy = 0; ++cz; break;
  3900             case ';' : cx = 0; ++cy; break;
  3901             default : ++cx;
  3903             nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
  3904           } else break;
  3906         if (nb==1) print("Input image %dx%dx%dx%d",img.dimx(),img.dimy(),img.dimz(),img.dimv());
  3907         else print("Input %d images %dx%d",nb,img.dimx(),img.dimy(),img.dimz(),img.dimv());
  3908         input_images.insert(nb,img); filenames.insert(nb,CImg<char>("(gmic)",7,1,1,1,false));
  3909       } else {
  3911         // Insert image as a loaded filename.
  3912         char filename[4096] = { 0 }, options[4096] = { 0 }; const char *ext = 0, *basename = 0;
  3913         if (argument[0]!='-' || (argument[1] && argument[1]!='.')) {
  3914           std::FILE *file = std::fopen(argument,"r");
  3915           if (file) { std::fclose(file); std::strcpy(filename,argument); }
  3916           else {
  3917             std::sscanf(argument,"%4095[^,],%s",filename,options);
  3918             if (!(file=std::fopen(filename,"r"))) {
  3919               if (filename[0]=='-') error("Input '%s' : Command not found.",filename);
  3920               else error("Input '%s' : File not found.",filename);
  3922             std::fclose(file);
  3924         } else std::strcpy(filename,argument);
  3925         basename = cimg::basename(filename);
  3926         ext = cimg::split_filename(filename);
  3928         if (!cimg::strcasecmp("off",ext)) {  // 3D object file.
  3929           print("Input 3D object '%s'",filename);
  3930           CImgList<unsigned int> primitives3d;
  3931           CImgList<unsigned char> colors3d;
  3932           CImg<float> opacities3d, points3d = CImg<float>::get_load_off(filename,primitives3d,colors3d);
  3933           opacities3d.assign(1,primitives3d.size,1,1,1);
  3934           points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
  3935           input_images.insert(1); points3d.transfer_to(input_images[0]);
  3936           input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
  3937                                             cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  3938           obj3d = true;
  3939         } else if (!cimg::strcasecmp(ext,"avi") ||
  3940                    !cimg::strcasecmp(ext,"mov") ||
  3941                    !cimg::strcasecmp(ext,"asf") ||
  3942                    !cimg::strcasecmp(ext,"divx") ||
  3943                    !cimg::strcasecmp(ext,"flv") ||
  3944                    !cimg::strcasecmp(ext,"mpg") ||
  3945                    !cimg::strcasecmp(ext,"m1v") ||
  3946                    !cimg::strcasecmp(ext,"m2v") ||
  3947                    !cimg::strcasecmp(ext,"m4v") ||
  3948                    !cimg::strcasecmp(ext,"mjp") ||
  3949                    !cimg::strcasecmp(ext,"mkv") ||
  3950                    !cimg::strcasecmp(ext,"mpe") ||
  3951                    !cimg::strcasecmp(ext,"movie") ||
  3952                    !cimg::strcasecmp(ext,"ogm") ||
  3953                    !cimg::strcasecmp(ext,"qt") ||
  3954                    !cimg::strcasecmp(ext,"rm") ||
  3955                    !cimg::strcasecmp(ext,"vob") ||
  3956                    !cimg::strcasecmp(ext,"wmv") ||
  3957                    !cimg::strcasecmp(ext,"xvid") ||
  3958                    !cimg::strcasecmp(ext,"mpeg")) {
  3959           unsigned int value0 = 0, value1 = 0, step = 1; char sep0 = 0, sep1 = 0, end = 0;
  3960           if ((std::sscanf(options,"%u%c%*c%u%c%*c%u%c",&value0,&sep0,&value1,&sep1,&step,&end)==5 && sep0=='%' && sep1=='%') ||
  3961               (std::sscanf(options,"%u%c%*c%u%*c%u%c",&value0,&sep0,&value1,&step,&end)==4 && sep0=='%') ||
  3962               (std::sscanf(options,"%u%*c%u%c%*c%u%c",&value0,&value1,&sep1,&step,&end)==4 && sep1=='%') ||
  3963               (std::sscanf(options,"%u%*c%u%*c%u%c",&value0,&value1,&step,&end)==3) ||
  3964               (std::sscanf(options,"%u%c%*c%u%c%c",&value0,&sep0,&value1,&sep1,&end)==4 && sep0=='%' && sep1=='%') ||
  3965               (std::sscanf(options,"%u%c%*c%u%c",&value0,&sep0,&value1,&end)==3 && sep0=='%') ||
  3966               (std::sscanf(options,"%u%*c%u%c%c",&value0,&value1,&sep1,&end)==3 && sep1=='%') ||
  3967               (std::sscanf(options,"%u%*c%u%c",&value0,&value1,&end)==2)) { // Read several frames
  3968             print("Input frames %u%s...%u%s with step %u of file '%s'",
  3969                   value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",step,filename);
  3970             if (sep0=='%' || sep1=='%') {
  3971               const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
  3972               if (sep0=='%') value0 = value0*nb_frames/100;
  3973               if (sep1=='%') value1 = value1*nb_frames/100;
  3975           } else if ((std::sscanf(options,"%u%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
  3976                      (std::sscanf(options,"%u%c",&value0,&end)==1)) { // Read one frame
  3977             print("Input frame %u%s of file '%s'",value0,sep0=='%'?"%":"",filename);
  3978             if (sep0=='%') {
  3979               const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
  3980               value0 = value0*nb_frames/100;
  3982             value1 = value0; step = 1;
  3983           } else { // Read all frames
  3984             print("Input all frames of file '%s'",filename);
  3985             value0 = 0; value1 = ~0U; sep0 = sep1 = 0; step = 1;
  3987           input_images.load_ffmpeg(filename,value0,value1,step);
  3988           if (input_images)
  3989             input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
  3990                                                                 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  3991         } else if (!cimg::strcasecmp("raw",ext)) { // Raw file.
  3992           int dx = 0, dy = 1, dz = 1, dv = 1;
  3993           if (std::sscanf(options,"%d%*c%d%*c%d%*c%d",&dx,&dy,&dz,&dv)>0) {
  3994             if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
  3995               error("Input raw file '%s' : Invalid specified dimensions %dx%dx%dx%d.",filename,dx,dy,dz,dv);
  3996             print("Input raw file '%s'",filename);
  3997             input_images.insert(1); input_images[0].load_raw(filename,dx,dy,dz,dv);
  3998             input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
  3999                                               cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  4000           } else error("Input raw file '%s' : Image dimensions must be specified.",filename);
  4001         } else if (!cimg::strcasecmp("yuv",ext)) { // YUV file.
  4002           int dx = 0, dy = 0; unsigned int first = 0, last = ~0U, step = 1;
  4003           if (std::sscanf(options,"%d%*c%d%*c%u%*c%u%*c%u",&dx,&dy,&first,&last,&step)>0) {
  4004             if (dx<=0 || dy<=0)
  4005               error("Input yuv file '%s' : Invalid specified dimensions %dx%d.",filename,dx,dy);
  4006             print("Input yuv file '%s'",filename);
  4007             input_images.load_yuv(filename,dx,dy,first,last,step);
  4008             input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
  4009                                                                 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  4010           } else error("Input yuv file '%s' : Image dimensions must be specified.",filename);
  4011         } else { // Other file type.
  4012           print("Input file '%s'",filename);
  4013           input_images.load(filename);
  4014           input_filenames.insert(input_images.size,
  4015                                  CImg<char>(is_fullpath?filename:basename,
  4016                                             cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
  4020       if (verbosity_level>=0) {
  4021         if (input_images) {
  4022           const unsigned int last = input_images.size-1;
  4023           if (obj3d)
  4024             std::fprintf(cimg_stdout," (%d points, %u primitives, %u colors).",
  4025                          (unsigned int)input_images(0,6),
  4026                          (unsigned int)input_images(0,7),
  4027                          (unsigned int)input_images(0,8));
  4028           else if (input_images.size==1)
  4029             std::fprintf(cimg_stdout," (1 image %ux%ux%ux%u).",
  4030                          input_images[0].width,input_images[0].height,input_images[0].depth,
  4031                          input_images[0].dim);
  4032           else std::fprintf(cimg_stdout," (%u images [0] = %ux%ux%ux%u, %s[%u] = %ux%ux%ux%u).",
  4033                             input_images.size,
  4034                             input_images[0].width,input_images[0].height,input_images[0].depth,
  4035                             input_images[0].dim,
  4036                             last==1?"":"...,",
  4037                             last,
  4038                             input_images[last].width,input_images[last].height,input_images[last].depth,
  4039                             input_images[last].dim);
  4040         } else std::fprintf(cimg_stdout," (no available data).");
  4043       for (unsigned int l = 0, siz = indices.size()-1, off = 0; l<=siz; ++l) {
  4044         const unsigned int ind = indices[l] + off;
  4045         if (l!=siz) images.insert(input_images,ind);
  4046         else {
  4047           images.insert(input_images.size,ind);
  4048           cimglist_for(input_images,k) images[ind+k].swap(input_images[k]);
  4050         filenames.insert(input_filenames,ind);
  4051         off+=input_images.size;
  4054     } catch (CImgException &e) {
  4055       const char *error_message = e.message;
  4056       char tmp[4096] = { 0 }, sep = 0;
  4057       if (std::sscanf(error_message,"%4095[^>]>:%c",tmp,&sep)==2 && sep==':') error_message+=cimg::strlen(tmp)+3;
  4058       error(error_message);
  4062   // Check if command line has grown too much (possible recursive macro calls).
  4063   if (command_line.size>=command_line_maxsize)
  4064     error("Command line overflow : There are too much commands specified (possible recursive macro substitution).");
  4066   // Check if some loops have not been terminated.
  4067   if (dowhile) warning("A '-while' directive is missing somewhere.");
  4068   if (repeatdone) warning("A '-done' directive is missing somewhere.");
  4070   // Display final result if necessary (not 'released' before).
  4071   if (images.size && !is_released) {
  4072     if (!display_objects3d(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),false))
  4073       display_images(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),true);
  4076   print("End G'MIC instance.\n");
  4077   return *this;
  4080 // Small hack to separate the compilation of G'MIC in different pixel types.
  4081 // (only intended to save computer memory when compiling !)
  4082 //--------------------------------------------------------------------------
  4083 #ifdef gmic_minimal
  4084 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
  4085 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  4086 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  4087 #else
  4088 #if defined(gmic_bool) || !defined(gmic_separate_compilation)
  4089 gmic& gmic::parse_bool(CImgList<bool>& images) { return parse(images); }
  4090 template gmic::gmic(const int, const char *const *const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
  4091 template gmic::gmic(const char* const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
  4092 #endif
  4093 #if defined(gmic_uchar) || !defined(gmic_separate_compilation)
  4094 gmic& gmic::parse_uchar(CImgList<unsigned char>& images) { return parse(images); }
  4095 template gmic::gmic(const int, const char *const *const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
  4096 template gmic::gmic(const char* const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
  4097 #endif
  4098 #if defined(gmic_char) || !defined(gmic_separate_compilation)
  4099 gmic& gmic::parse_char(CImgList<char>& images) { return parse(images); }
  4100 template gmic::gmic(const int, const char *const *const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
  4101 template gmic::gmic(const char* const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
  4102 #endif
  4103 #if defined(gmic_ushort) || !defined(gmic_separate_compilation)
  4104 gmic& gmic::parse_ushort(CImgList<unsigned short>& images) { return parse(images); }
  4105 template gmic::gmic(const int, const char *const *const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
  4106 template gmic::gmic(const char* const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
  4107 #endif
  4108 #if defined(gmic_short) || !defined(gmic_separate_compilation)
  4109 gmic& gmic::parse_short(CImgList<short>& images) { return parse(images); }
  4110 template gmic::gmic(const int, const char *const *const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
  4111 template gmic::gmic(const char* const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
  4112 #endif
  4113 #if defined(gmic_uint) || !defined(gmic_separate_compilation)
  4114 gmic& gmic::parse_uint(CImgList<unsigned int>& images) { return parse(images); }
  4115 template gmic::gmic(const int, const char *const *const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
  4116 template gmic::gmic(const char* const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
  4117 #endif
  4118 #if defined(gmic_int) || !defined(gmic_separate_compilation)
  4119 gmic& gmic::parse_int(CImgList<int>& images) { return parse(images); }
  4120 template gmic::gmic(const int, const char *const *const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
  4121 template gmic::gmic(const char* const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
  4122 #endif
  4123 #if defined(gmic_float) || !defined(gmic_separate_compilation)
  4124 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
  4125 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  4126 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
  4127 #endif
  4128 #if defined(gmic_double) || !defined(gmic_separate_compilation)
  4129 gmic& gmic::parse_double(CImgList<double>& images) { return parse(images); }
  4130 template gmic::gmic(const int, const char *const *const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
  4131 template gmic::gmic(const char* const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
  4132 #endif
  4133 #endif
  4134 #endif
  4136 //-----------------------
  4137 // Start main procedure.
  4138 //-----------------------
  4139 #if defined(gmic_main) || (!defined(gmic_separate_compilation) && !defined(gmic_minimal))
  4140 extern char data_def[];
  4142 int main(int argc, char **argv) {
  4144   // Display help if necessary.
  4145   //---------------------------
  4146   if (argc==1) {
  4147     std::fprintf(cimg_stdout,"<gmic> No options or data provided. Try '%s -h' for help.\n",cimg::basename(argv[0]));
  4148     std::exit(0);
  4151   if (cimg_option("-h",false,0) || cimg_option("-help",false,0) || cimg_option("--help",false,0)) {
  4152     cimg_usage("GREYC's Magic Image Converter");
  4154     char version[1024] = { 0 };
  4155     std::sprintf(version,"        Version %d.%d.%d.%d, Copyright (C) 2008-2009, David Tschumperle (http://gmic.sourceforge.net)",
  4156                  gmic_version/1000,(gmic_version/100)%10,(gmic_version/10)%10,gmic_version%10);
  4157     cimg_help(version);
  4159     cimg_help("\n  Usage\n"
  4160               "  -----");
  4161     cimg_help("  gmic [file_1] [file_2] .. [file_n] [command_1] .. [command_n] [file_n+1] ...\n");
  4162     cimg_help("  G'MIC is an interpreter of image processing macros whose goal is to convert, manipulate and");
  4163     cimg_help("  visualize generic 1D/2D/3D multi-spectral image and video files. It follows these simple rules :\n");
  4164     cimg_help("    - G'MIC handles a numbered list of images which are all stored in computer memory.");
  4165     cimg_help("    - The first image of the list has indice '[0]'.");
  4166     cimg_help("    - Negative indices are treated in a cyclic way (i.e. '[-1]' is the last image,");
  4167     cimg_help("      '[-2]' the penultimate one, and so on...).");
  4168     cimg_help("    - Command line items tell how to add/remove/manipulate/display images of the list.");
  4169     cimg_help("    - Items are read and executed in the order they appear on the command line, from the left to the right.");
  4170     cimg_help("    - Items can thus appear more than one time on the command line.");
  4171     cimg_help("    - An item starting by '-' is a G'MIC instruction.");
  4172     cimg_help("    - One instruction may have two equivalent names (regular and short).");
  4173     cimg_help("    - A G'MIC instruction may have mandatory or optional arguments.");
  4174     cimg_help("    - When multiple arguments are needed, they are separated by commas ','.");
  4175     cimg_help("    - Items that are not instructions are considered either as input filenames or input strings.");
  4176     cimg_help("    - When an input filename is encountered, the corresponding image data are loaded");
  4177     cimg_help("      and added to the end of the image list.");
  4178     cimg_help("      (see section 'Filename options' below for more informations on file input/output).");
  4179     cimg_help("    - Special filenames '-' or '-.ext' mean 'standard input/output' (optionally. in 'ext' format).");
  4180     cimg_help("    - Special input strings can be used to insert new images to the list. They can be :");
  4181     cimg_help("        - 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]' : Insert 'N' black images with specified size.");
  4182     cimg_help("          (adding '%' to a dimension means 'percentage of to the same dimension in the last image'),");
  4183     cimg_help("        - '[indice]' or '[indice]xN' : Insert 1 or N copies of the existing image [indice].");
  4184     cimg_help("        - '(v1,v2,...)' or '(v1,v2,...)xN' : Insert 1 or N copies of the specified IxJxKxL image.");
  4185     cimg_help("          Separators inside '(..)' can be ',' (column), ';' (line), '/' (slice) or '^' (channel).");
  4186     cimg_help("    - A G'MIC instruction may be restricted to a specific subset of the list, by adding '[subset]' to");
  4187     cimg_help("      the instruction name. Several usual expressions are possible for 'subset', for instance : ");
  4188     cimg_help("        '-command[0,1,3]' : Apply instruction on images 0,1 and 3.");
  4189     cimg_help("        '-command[3-5]' : Apply instruction on images 3 to 5.");
  4190     cimg_help("        '-command[50%-100%] : Apply instruction on the second half of the image list.");
  4191     cimg_help("        '-command[0,-2,-1]' : Apply instruction on the first and two latest images.");
  4192     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).");
  4193     cimg_help("        '-command[0,2-4,50%--1]' : Apply instruction on images 0,2,3,4 and the second half of the list.");
  4194     cimg_help("    - When no image subset is specified, a G'MIC instruction is applied on all images of the list.");
  4195     cimg_help("    - Native (non-macro) instructions starting with '--' instead of '-' do not act 'in-place' but");
  4196     cimg_help("      insert their result as a new image at the end of the list.");
  4197     cimg_help("    - On the command line, any item of the form '@indice' or '@{indice}' is replaced");
  4198     cimg_help("      by the values of the image '[indice]' separated by commas.");
  4199     cimg_help("    - Items '@?' (or '@{?}'), '@{?,max}' or '@{?,min,max}' are replaced by a float random value between [0,1], [0,max] or [min,max].");
  4200     cimg_help("    - Items '@[?]', '@[?,max]' or '@[?,min,max]' do the same but return integer random values.");
  4201     cimg_help("    - Restrictions to a subset of image values can be specified with '@{indice,subset}' (as in '@{2,0-50%}').");
  4202     cimg_help("    - Restriction to a particular pixel coordinate can be specified with '@(indice,x[,y[,z[,v[,borders]]]])'.");
  4203     cimg_help("    - On the command line, the item '@#' is replaced by the number of images in the list.");
  4204     cimg_help("    - Input filenames or commands may result to the generation of 3D objects.");
  4205     cimg_help("    - A 3D object viewer, based on software rendering, is included in G'MIC.");
  4206     cimg_help("    - A 3D object is stored as a single-column image containing all object data, in the following order :");
  4207     cimg_help("        { magic header, vertices, faces, colors, opacities }.");
  4208     cimg_help("    - Custom user-defined G'MIC instructions can be defined with the use of a macro file.");
  4209     cimg_help("    - A macro file is a simple ASCII text file, each line being of the form");
  4210     cimg_help("        'instruction_name : substitution' or 'substitution (continuation)' or '# comment'.");
  4211     cimg_help("    - Each invoked macro instruction is substituted as its defined content on the command line.");
  4212     cimg_help("    - A macro file 'gmic_def.raw' is distributed within the G'MIC package.");
  4213     cimg_help("    - The macros defined in 'gmic_def.raw' are included by default in G'MIC.");
  4214     cimg_help("    - Macros arguments, separated by commas, can be added after the invokation of a macro instruction.");
  4215     cimg_help("    - In macros definitions, expressions starting with '$' are used to reference macro arguments :");
  4216     cimg_help("        $i and ${i} are replaced by the value of the i-th macro argument.");
  4217     cimg_help("        $# and ${#} are replaced by the number of macro arguments.");
  4218     cimg_help("        $* and ${*} are replaced by the entire string of macro arguments.");
  4219     cimg_help("        ${i*} is replaced by all macro arguments following the i-th argument (included).");
  4220     cimg_help("        ${i=$#} is replaced by $i if defined, or by its new value $# else.");
  4221     cimg_help("        ${i=$j} is replaced by $i if defined, or by its new value $j else.");
  4222     cimg_help("        ${i=default} is replaced by $i if defined, or by its new value 'default' else.");
  4223     cimg_help("\n  A list of available native and macro instructions is available below.");
  4224     cimg_help("  A parameter specified in '[]' is optional, except when standing for '[indices]' where it");
  4225     cimg_help("  corresponds to one or several indices of the image list, as described above. In this case, the '[' and ']'");
  4226     cimg_help("  characters must explicitly appear when writting the item.");
  4228     cimg_help("\n  Global options\n"
  4229               "  --------------");
  4230     cimg_option("-help","(no args)","Display this help (eq. to '-h').");
  4231     cimg_option("-verbose+","(no args)","Increment verbosity level (eq. to '-v+').");
  4232     cimg_option("-verbose-","(no args)","Decrement verbosity level (eq. to '-v-').");
  4233     cimg_option("-macros","'filename'","Load macro file from specified filename (eq. to '-m').");
  4234     cimg_option("-debug","(no args)","Switch debug flag (when on, displays internal infos for debugging).");
  4235     cimg_option("-fullpath","(no args)","Switch full path flag (when on, displays full filename paths).");
  4237     cimg_help("\n  Arithmetic operators\n"
  4238               "  --------------------");
  4239     cimg_option("-add","'value', '[indice]' or (no args)","Add 'value' or '[indice]' to image(s)");
  4240     cimg_help("                                              "
  4241               "or add image(s) together (eq. to '-+').");
  4242     cimg_option("-sub","'value', '[indice]' or (no args)","Substract 'value' or '[indice]' to image(s)");
  4243     cimg_help("                                              "
  4244               "or substract image(s) together (eq. to '--').");
  4245     cimg_option("-mul","'value', '[indice]' or (no args)","Multiply image(s) by 'value' or '[indice]'");
  4246     cimg_help("                                              "
  4247               "or multiply image(s) together (eq. to '-*').");
  4248     cimg_option("-div","'value', '[indice]' or (no args)","Divide image(s) by 'value' or '[indice]'");
  4249     cimg_help("                                              "
  4250               "or divide image(s) together (eq. to '-/').");
  4251     cimg_option("-pow","'value', '[indice]' or (no args)","Compute image(s) to the power of 'value' or '[indice]'");
  4252     cimg_help("                                              "
  4253               "or power of the image(s) together (eq. to '-^').");
  4254     cimg_option("-min","'value', '[indice]' or (no args)","Compute minimum between image(s) and 'value' or '[indice]'");
  4255     cimg_help("                                              "
  4256               "or minimum of image(s) together.");
  4257     cimg_option("-max","'value', '[indice]' or (no args)","Compute maximum between image(s) and 'value' or '[indice]'");
  4258     cimg_help("                                              "
  4259               "or maximum of image(s) together.");
  4260     cimg_option("-mod","'value', '[indice]' or (no args)","Compute modulo of image(s) with 'value' or '[indice]'");
  4261     cimg_help("                                              "
  4262               "or modulo with image(s) together.");
  4263     cimg_option("-and","'value', '[indice]' or (no args)","Compute bitwise AND of image(s) with 'value' or '[indice]'");
  4264     cimg_help("                                              "
  4265               "or bitwise AND of image(s) together.");
  4266     cimg_option("-or","'value', '[indice]' or (no args)","Compute bitwise OR of image(s) with 'value' or '[indice]'");
  4267     cimg_help("                                              "
  4268               "or bitwise OR of image(s) together.");
  4269     cimg_option("-xor","'value', '[indice]' or (no args)","Compute bitwise XOR of image(s) with 'value' '[indice]'");
  4270     cimg_help("                                              "
  4271               "or bitwise XOR of image(s) together.");
  4272     cimg_option("-cos","(no args)","Compute cosine of image(s) values.");
  4273     cimg_option("-sin","(no args)","Compute sine of image(s) values.");
  4274     cimg_option("-tan","(no args)","Compute tangent of image(s) values.");
  4275     cimg_option("-acos","(no args)","Compute arccosine of image(s) values.");
  4276     cimg_option("-asin","(no args)","Compute arcsine of image(s) values.");
  4277     cimg_option("-atan","(no args)","Compute arctangent of image(s) values.");
  4278     cimg_option("-abs","(no args)","Compute absolute value of image(s) values.");
  4279     cimg_option("-sqr","(no args)","Compute square of image(s) values.");
  4280     cimg_option("-sqrt","(no args)","Compute square root of image(s) values.");
  4281     cimg_option("-exp","(no args)","Compute exponential of image(s) values.");
  4282     cimg_option("-log","(no args)","Compute logarithm of image(s) values.");
  4283     cimg_option("-log10","(no args)","Compute logarithm_10 of image(s) values.");
  4285     cimg_help("\n  Pointwise pixel manipulation\n"
  4286               "  ----------------------------");
  4287     cimg_option("-type","'value_type'","Cast all images into specified value type (eq. to '-t').");
  4288     cimg_help("                                              "
  4289               "('value_type' can be 'bool','uchar','char','ushort','short',");
  4290     cimg_help("                                              "
  4291               "'uint','int','float','double').");
  4292     cimg_option("-set","'value[,x[,y[,z[,v]]]]'","Set scalar value in image(s) at specified position (eq. to '-=').");
  4293     cimg_option("-endian","(no args)","Invert endianness of the image(s) buffers.");
  4294     cimg_option("-fill","'value1,value2,...'","Fill image(s) with scalar values in a repetitive way (eq. to '-f').");
  4295     cimg_option("-threshold","'value[%][,soft]' or (noargs)","Threshold pixel values ((noargs) for interactive mode).");
  4296     cimg_option("-cut","'{value1[%],[indice]},{value2[%],[indice]}' or (noargs)","Cut pixel values in specified range ");
  4297     cimg_help("                                              "
  4298               "((noargs) for interactive mode).");
  4299     cimg_option("-normalize","'{value1[%],[indice]},{value2[%],[indice]}'",
  4300                 "Normalize pixel values in specified range (eq. to '-n').");
  4301     cimg_option("-round","'round_value[,round_type]'","Round pixel values.");
  4302     cimg_option("-equalize","'nb_levels'","Equalize image(s) histogram(s) using specified number of levels.");
  4303     cimg_option("-quantize","'nb_levels'","Quantize image(s) with 'nb_levels' levels.");
  4304     cimg_option("-noise","'std[%][,noise_type]'","Add noise with specified standard deviation");
  4305     cimg_help("                                              "
  4306               "('noise_type' can be '{0=gaussian, 1=uniform, 2=salt&pepper, 3=poisson}'.");
  4307     cimg_option("-norm","(no args)","Compute pointwise L2-norm of vector-valued image(s).");
  4308     cimg_option("-orientation","(no args)","Compute pointwise orientation of vector-valued image(s).");
  4310     cimg_help("\n  Color bases conversions\n"
  4311               "  -----------------------");
  4312     cimg_option("-rgb2hsv","(no args)","Convert image(s) from RGB to HSV colorbases.");
  4313     cimg_option("-rgb2hsl","(no args)","Convert image(s) from RGB to HSL colorbases.");
  4314     cimg_option("-rgb2hsi","(no args)","Convert image(s) from RGB to HSI colorbases.");
  4315     cimg_option("-rgb2yuv","(no args)","Convert image(s) from RGB to YUV colorbases.");
  4316     cimg_option("-rgb2ycbcr","(no args)","Convert image(s) from RGB to YCbCr colorbases.");
  4317     cimg_option("-rgb2xyz","(no args)","Convert image(s) from RGB to XYZ colorbases.");
  4318     cimg_option("-rgb2lab","(no args)","Convert image(s) from RGB to Lab colorbases.");
  4319     cimg_option("-rgb2cmy","(no args)","Convert image(s) from RGB to CMY colorbases.");
  4320     cimg_option("-rgb2cmyk","(no args)","Convert image(s) from RGB to CMYK colorbases.");
  4321     cimg_option("-rgb2lut","'[indice][,dithering]' or 'LUT_type[,dithering]'","Index image(s) with color palette ");
  4322     cimg_help("                                              "
  4323               "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'");
  4324     cimg_help("                                              "
  4325               "'dithering' can be '{0=off, 1=on}').");
  4326     cimg_option("-hsv2rgb","(no args)","Convert image(s) from HSV to RGB colorbases.");
  4327     cimg_option("-hsl2rgb","(no args)","Convert image(s) from HSL to RGB colorbases.");
  4328     cimg_option("-hsi2rgb","(no args)","Convert image(s) from HSI to RGB colorbases.");
  4329     cimg_option("-yuv2rgb","(no args)","Convert image(s) from YUV to RGB colorbases.");
  4330     cimg_option("-ycbcr2rgb","(no args)","Convert image(s) from YCbCr to RGB colorbases.");
  4331     cimg_option("-xyz2rgb","(no args)","Convert image(s) from XYZ to RGB colorbases.");
  4332     cimg_option("-lab2rgb","(no args)","Convert image(s) from Lab to RGB colorbases.");
  4333     cimg_option("-cmy2rgb","(no args)","Convert image(s) from CMY to RGB colorbases.");
  4334     cimg_option("-cmyk2rgb","(no args)","Convert image(s) from CMYK to RGB colorbases.");
  4335     cimg_option("-lut2rgb","'[indice]' or 'LUT_type'","Map color palette to image(s) ");
  4336     cimg_help("                                              "
  4337               "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'.");
  4339     cimg_help("\n  Geometric manipulation\n"
  4340               "  ----------------------");
  4341     cimg_option("-resize","'[indice][,interpolation[,borders[,center]]]' or ","");
  4342     cimg_help("                     "
  4343               "'{[indice],width[%]}[x{[indice],height[%]}[x{[indice],depth[%]}[x{[indice],dim[%]}[,interpolation[,borders[,center]]]]]]'");
  4344     cimg_help("                                              "
  4345               "or (noargs)");
  4346     cimg_help("                                              "
  4347               "Resize image(s) to specified geometry ((noargs) for interactive mode) (eq. to '-r')");
  4348     cimg_help("                                              "
  4349               "('interpolation' can be '{0=none, 1=nearest, 2=average, 3=linear, 4=grid, 5=cubic}').");
  4350     cimg_option("-resize2x","(no args)","Resize image(s) with Scale2x.");
  4351     cimg_option("-resize3x","(no args)","Resize image(s) with Scale3x.");
  4352     cimg_option("-crop","'x0[%],x1[%][,border_conditions]' or 'x0[%],y0[%],x1[%],y1[%][,border_conditions]' or ","");
  4353     cimg_help("                                              "
  4354               "'x0[%],y0[%],z0[%],x1[%],y1[%],z1[%][,border_conditions]' or ");
  4355     cimg_help("                                              "
  4356               "'x0[%],y0[%],z0[%],v0[%],x1[%],y1[%],z1[%],v1[%][,border_conditions]' or (noargs).");
  4357     cimg_help("                                              "
  4358               "Crop image(s) using specified geometry ((noargs) for interactive mode) (eq. to '-c') ");
  4359     cimg_help("                                              "
  4360               "('border_conditions' can be '{0=zero, 1=nearest}')");
  4361     cimg_help("                                              "
  4362               "((no args) for interactive mode).");
  4363     cimg_option("-autocrop","'value1,value2,...'","Autocrop image(s) using specified background color.");
  4364     cimg_option("-channels","'{[ind0],v0[%]}[,{[ind1],v1[%]}]'","Select channels v0..v1 of multi-spectral image(s).");
  4365     cimg_option("-slices","'{[ind0],z0[%]}[,{[ind1],z1[%]}]'","Select slices z0..z1 of volumetric image(s).");
  4366     cimg_option("-lines","'{[ind0],y0[%]}[,{[ind1],y1[%]}]'","Select lines y0..y1 of image(s).");
  4367     cimg_option("-columns","'{[ind0],x0[%]}[,{[ind1],x1[%]}]'","Select columns x0..x1 of image(s).");
  4368     cimg_option("-rotate","'angle[,border_conditions]'","Rotate image(s) with a given angle ");
  4369     cimg_help("                                              "
  4370               "('border_conditions' can be '{-3=cyclic (in-place), -2=nearest(ip), -1=zero(ip), 0=zero, 1=nearest, 2=cyclic}'");
  4371     cimg_help("                                              "
  4372               "and 'interpolation' can be '{0=none, 1=linear, 2=cubic}').");
  4373     cimg_option("-mirror","'axis'",
  4374                 "Mirror image(s) along specified axis ('axis' can be '{x,y,z,v}').");
  4375     cimg_option("-translate","'tx[%][,ty[%][,tz[%][,tv[%][,border_conditions]]]]'",
  4376                 "Translate image(s) by vector (dx,dy,dz,dv)");
  4377     cimg_help("                                              "
  4378               "('border_conditions' can be '{0=zero, 1=nearest, 2=cyclic}').");
  4379     cimg_option("-transpose","(no args)","Transpose image(s).");
  4380     cimg_option("-invert","(no args)","Compute inverse matrix.");
  4381     cimg_option("-permute","'permutation'","Permute image axes by the specified permutation "
  4382                 "('permutation' can be 'yxzv',...).");
  4383     cimg_option("-unroll","'axis'",
  4384                 "Unroll image(s) along specified axis ('axis' can be '{x,y,z,v}').");
  4385     cimg_option("-split","'axis[,nb_parts]' or 'value[,keep]'",
  4386                 "Split image(s) along specified axis or value ('axis' can be '{x,y,z,v}') (eq. to '-s').");
  4387     cimg_option("-append","'axis,[alignement]'","Append image(s) along specified axis and alignement (eq. to '-a')");
  4388     cimg_help("                                              "
  4389               "('axis' can be '{x,y,z,v}' and 'alignement' can be '{p=left, c=center, n=right)'.");
  4390     cimg_option("-warp","'[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]'",
  4391                 "Warp image(s) in 'nb_frames' with field '[indice]' ");
  4392     cimg_help("                                              "
  4393               "('relative' can be '{0,1}', 'interpolation' can be '{0,1}', "
  4394               "'border_conditions' can be '{0=zero, 1=nearest}').");
  4396     cimg_help("\n  Image filtering\n"
  4397               "  ---------------");
  4398     cimg_option("-blur","'std[,border_conditions]'",
  4399                 "Apply gaussian blur of specified standard deviation");
  4400     cimg_help("                                              "
  4401               "('border_conditions' can be '{0=zero, 1=nearest}').");
  4402     cimg_option("-bilateral","'stdevs,stdevr'",
  4403                 "Apply bilateral filtering with specified standard deviations 'stdevs' and 'stdevr'.");
  4404     cimg_option("-smooth","'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]'","");
  4405     cimg_help("                                              "
  4406               "Smooth image(s) anisotropically with specified parameters.");
  4407     cimg_option("-denoise","'patch_size[,stdev_p[,stdev_s[,lookup_size]]]'",
  4408                 "Denoise image(s) with a patch-averaging procedure.");
  4409     cimg_option("-median","'size'","Apply median filter with specified size.");
  4410     cimg_option("-sharpen","'amplitude[,0]' or 'amplitude,1[,edge[,alpha[,sigma]]]'",
  4411                 "Sharpen image(s) with inverse diffusion or shock filters.");
  4412     cimg_option("-convolve","'[indice][,border_conditions]'",
  4413                 "Convolve image(s) by the specified mask");
  4414     cimg_help("                                              "
  4415               "('border_conditions' can be '{0=zero, 1=nearest}').");
  4416     cimg_option("-correlate","'[indice][,border_conditions]'",
  4417                 "Correlate image(s) by the specified mask (same parameters as above).");
  4418     cimg_option("-erode","'size[,border_conditions]' or '[indice][,border_conditions]'","");
  4419     cimg_help("                                              "
  4420               "Erode image(s) by the specified mask (same parameters as above)').");
  4421     cimg_option("-dilate","'size[,border_conditions]' or '[indice][,border_conditions]'","");
  4422     cimg_help("                                              "
  4423               "Dilate image(s) by the specified mask (same parameters as above).");
  4424     cimg_option("-gradient","'x', 'xy', 'xyz' or (no args)","Compute image gradient.");
  4425     cimg_option("-hessian","'{xx,xy,xz,yy,yz,zz}' or (no args)","Compute image Hessian.");
  4426     cimg_option("-fft","(no args)","Compute direct Fourier transform.");
  4427     cimg_option("-ifft","(no args)","Compute inverse Fourier transform.");
  4429     cimg_help("\n  Image creation and drawing\n"
  4430               "  --------------------------");
  4431     cimg_option("-dimensions","(no args)","Get dimensions of the image(s) as a 1x4 vector.");
  4432     cimg_option("-stats","(no args)","Get statistics of the image(s) as a 1x6 vector.");
  4433     cimg_option("-histogram","'nb_values[%]'","Compute histogram of image(s) with 'nb_values' values.");
  4434     cimg_option("-distance","'isovalue'","Compute distance function(s) to specified isovalue.");
  4435     cimg_option("-hamilton","'nb_iter[,band_size]'","Apply Hamilton-Jacobi PDE to compute distance to 0.");
  4436     cimg_option("-label","(no args)","Label connected components of image(s).");
  4437     cimg_option("-displacement","'[indice][,smoothness[,precision[,nbscales[,itermax]]]]","Estimate smooth displacement field between image(s) "
  4438                 "and specified target '[indice]'.");
  4439     cimg_option("-sort","(no args)","Sort values of image(s) in increasing order.");
  4440     cimg_option("-psnr","'max_value' or (noargs)","Compute PSNR between specified image(s).");
  4441     cimg_option("-point","'x[%],y[%][,z[%][,opacity[,color]]]'","Draw 3D colored point on specified image(s).");
  4442     cimg_option("-line","'x0[%],y0[%],x1[%],y1[%][,opacity[,color]]'","Draw 2D colored line on specified image(s).");
  4443     cimg_option("-polygon","'N,x0[%],y0[%],..,xN[%],yN[%][,opacity[,color]]'","Draw a 2D colored N-vertices polygon on specified image(s).");
  4444     cimg_option("-ellipse","'x[%],y[%],r,R[,u,v[,opacity[,color]]]'","Draw 2D colored ellipse on specified image(s).");
  4445     cimg_option("-text","text,x[%],y[%],size[,opacity[,color]]'",
  4446                 "Draw specified text at position (x,y) and with specified font size.");
  4447     cimg_option("-image","'[indice][,x[%][,y[%][,z[%][,opacity[,ind_mask]]]]]'","Draw sprite image on specified image(s).");
  4448     cimg_option("-object3d","'[indice][,x[%][,y[%][,z[,opacity]]]]'","Draw 3D object on specified image(s).");
  4449     cimg_option("-plasma","'alpha[,beta[,opacity]]'","Draw plasma on specified image(s).");
  4450     cimg_option("-mandelbrot","'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]'","Draw Mandelbrot/Julia fractals on specified image(s).");
  4451     cimg_option("-flood","'x[%][,y[%][,z[%][,tolerance[,opacity[,color]]]]]'",
  4452                 "Flood-fill image(s) starting from (x,y,z) with specified tolerance.");
  4454     cimg_help("\n  List manipulation\n"
  4455               "  -----------------");
  4456     cimg_option("-remove","(no args)","Remove image(s) from list (eq. to '-rm').");
  4457     cimg_option("-keep","(no args)","Keep only specified image(s) (eq. to '-k').");
  4458     cimg_option("-move","'position'","Move image(s) at specified position (eq. to '-mv').");
  4459     cimg_option("-reverse","(no args)","Reverse image(s) order.");
  4460     cimg_option("-name","\"name\"","Set display name of image(s).");
  4462     cimg_help("\n  3D Rendering\n"
  4463               "  ------------");
  4464     cimg_option("-cube3d","'size'","Insert a 3D cube at the end of the list.");
  4465     cimg_option("-cone3d","'radius[,height[,subdivisions]]'","Insert a 3D cube at the end of the list.");
  4466     cimg_option("-cylinder3d","'radius[,height[,subdivisions]]'","Insert a 3D cylinder at the end of the list.");
  4467     cimg_option("-torus3d","'radius1,radius2[,subdivisions1,subdivisions2]'","Insert a 3D torus at the end of the list.");
  4468     cimg_option("-plane3d","'sizex,sizey[,subdivisionsx,subdisivionsy]'","Insert a 3D plane at the end of the list.");
  4469     cimg_option("-sphere3d","'radius[,subdivisions]'","Insert a 3D sphere at the end of the list.");
  4470     cimg_option("-elevation3d","'z-factor' or '[indice]'",
  4471                 "Generate 3D elevation(s) of image(s) using specified z-factor or elevation map.");
  4472     cimg_option("-isovalue3d","'value'","Generate 3D object(s) by retrieving isophote or isosurface of image(s).");
  4473     cimg_option("-center3d","(no args)","Center 3D object(s) (eq. to '-c3d').");
  4474     cimg_option("-normalize3d","(no args)","Normalize 3D object(s) to a unit size (eq. to '-n3d').");
  4475     cimg_option("-rotate3d","'u,v,w,angle'","Rotate 3D object(s) around axis (u,v,w) with specified angle (eq. to '-rot3d').");
  4476     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').");
  4477     cimg_option("-sub3d","'tx,ty,tz'","Translate 3D object(s) with the opposite of the specified vector (eq. to '--3d').");
  4478     cimg_option("-mul3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified factor (eq. to '-*3d').");
  4479     cimg_option("-div3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified inverse factor (eq. to '-/3d').");
  4480     cimg_option("-color3d","'R,G,B[,opacity]'","Set color of 3D object(s) (eq. to '-col3d').");
  4481     cimg_option("-opacity3d","'opacity'","Set opacity of 3D object(s) (eq. to '-opac3d').");
  4482     cimg_option("-invert3d","(no args)","Invert primitive orientations of 3D object(s) (eq. to '-i3d').");
  4483     cimg_option("-split3d","(no args)","Split 3D object data into 6 data vectors 'header,N,vertices,primitives,colors,opacities' (eq. to '-s3d').");
  4484     cimg_option("-light3d","'posx,posy,posz'","Set the 3D position of the light for 3D rendering (eq. to '-l3d').");
  4485     cimg_option("-focale3d","'value'","Set focale value for 3D rendering (eq. to '-f3d').");
  4486     cimg_option("-specl3d","'value'","Set amount of specular light for 3D rendering (eq. to '-sl3d').");
  4487     cimg_option("-specs3d","'value'","Set shininess of specular light for 3D rendering (eq. to '-ss3d').");
  4488     cimg_option("-orient3d","(no args)","Switch double-sided mode for 3D rendering (eq. to '-o3d').");
  4489     cimg_option("-render3d","'mode'","Set 3D rendering mode");
  4490     cimg_help("                                              "
  4491               "(can be '{-1=bounding-box, 0=pointwise, 1=linear, 2=flat, 3=flat-shaded, 4=Gouraud-shaded, 5=Phong-shaded}') (eq. to '-r3d').");
  4492     cimg_option("-renderd3d","'mode'","Set dynamic rendering mode in 3D viewer (same values as above) (eq. to '-rd3d').");
  4493     cimg_option("-background3d","'R,G,B'","Define background color in 3D viewer (eq. to '-b3d').");
  4495     cimg_help("\n  Program controls\n"
  4496               "  ----------------");
  4497     cimg_option("-nop","(no args)","Do nothing.");
  4498     cimg_option("-skip","(any args)","Do nothing but skip the next argument.");
  4499     cimg_option("-echo","'text'","Output specified message (eq. to '-e').");
  4500     cimg_option("-print","(no args)","Print image(s) informations (eq. to '-p').");
  4501     cimg_option("-quit","(no args)","Force interpreter to quit (eq. to '-q').");
  4502     cimg_option("-do","(no args)","Start a 'do-while' code bloc.");
  4503     cimg_option("-while","'cond'","End a 'do-while' code bloc and go back to associated '-do' if 'cond' is a strictly positive value.");
  4504     cimg_option("-if","'cond'","Start a 'if-then-else' code bloc and test if 'cond' is a strictly positive value.");
  4505     cimg_option("-else","(no args)","Execute following commands if previous '-if' condition failed.");
  4506     cimg_option("-endif","(no args)","End a 'if-then-else' code bloc");
  4507     cimg_option("-repeat","'N'","Start a 'repeat-done' code bloc.");
  4508     cimg_option("-done","(no args)","End a 'repeat-done' code bloc, and go to associated '-repeat' if iterations remain.");
  4509     cimg_option("-int","'arg1,...,argN'","Check if all specified arguments are integer. If not, print an error message and exit.");
  4510     cimg_option("-float","'arg1,...,argN","Check if all specified arguments are float values. If not, print an error message and exit.");
  4512     cimg_help("\n  Input/output\n"
  4513               "  ------------");
  4514     cimg_option("-input","'filename' or 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]'","");
  4515     cimg_help("                     "
  4516               "or '[indice][xN]' or '(v11{,;/^}v21...vLM)[xN]'");
  4517     cimg_help("                                              "
  4518               "Input filename, empty image, image copy, or image with specified values (eq. to '-i' or (no args)).");
  4519     cimg_option("-output","'filename'","Output image(s) in specified filename (eq. to '-o').");
  4520     cimg_option("-display","(no args)","Display image(s) (eq. to '-d').");
  4521     cimg_option("-display3d","(no args)","Display 3D object(s) (eq. to '-d3d').");
  4522     cimg_option("-plot","'[plot_type[,vertex_type[,xmin[,xmax[,ymin[,ymax]]]]]]'",
  4523                 "Display image(s) as 1D plot(s)");
  4524     cimg_help("                                              "
  4525               "('plot_type' can be '{0=none, 1=lines, 2=splines, 3=bar}').");
  4526     cimg_help("                                              "
  4527               "('vertex_type' can be in '[0-7]').");
  4528     cimg_option("-select","'select_type'","Select feature from image(s) in an interactive way");
  4529     cimg_help("                                              "
  4530               "('select_type' can be in '{0=point, 1=line, 2=rectangle, 3=circle').");
  4532     // Print descriptions of default macros.
  4533     char line[256*1024] = { 0 }, name[4096] = { 0 }, args[4096] = { 0 }, desc[4096] = { 0 };
  4534     bool first_description = true;
  4535     for (const char *data = data_def; *data; ) {
  4536       if (*data=='\n') ++data;
  4537       else {
  4538         if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line);
  4539         if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
  4540           if (first_description) cimg_help("\n  Commands : Default macros\n"
  4541                                            "  -------------------------");
  4542           std::fprintf(cimg_stdout,"%s    %s-%-15s%s %-24s %s%s%s",
  4543                        first_description?"":"\n",
  4544                        cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
  4545           first_description = false;
  4550     // Print descriptions of use-defined macros.
  4551     first_description = true;
  4552     for (int i = 1; i<argc-1; ++i) if (!cimg::strcmp("-m",argv[i]) || !cimg::strcmp("-macros",argv[i])) {
  4553       std::FILE *file = cimg::fopen(argv[i+1],"r");
  4554       if (file) {
  4555         int err = 0;
  4556         while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
  4557           if (err) { // Non empty-line
  4558             name[0] = args[0] = desc[0] = 0;
  4559             if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
  4560               if (first_description) cimg_help("\n\n  Commands : User-defined macros\n"
  4561                                                "  ------------------------------");
  4562               std::fprintf(cimg_stdout,"%s    %s-%-15s%s %-24s %s%s%s",
  4563                            first_description?"":"\n",
  4564                            cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
  4565               first_description = false;
  4570       cimg::fclose(file);
  4573     cimg_help("\n\n  Viewers shortcuts\n"
  4574               "  -----------------");
  4575     cimg_help("  When displaying image(s) or 3D object(s) with G'MIC, you can use these shortcuts in viewers :");
  4576     cimg_help("   - CTRL+D : Increase window size.");
  4577     cimg_help("   - CTRL+C : Decrease window size.");
  4578     cimg_help("   - CTRL+R : Reset window size.");
  4579     cimg_help("   - CTRL+F : Toggle fullscreen mode.");
  4580     cimg_help("   - CTRL+S : Save current window snapshot.");
  4581     cimg_help("   - CTRL+O : Save current instance of viewed image or 3D object.\n");
  4582     cimg_help("  Specific options for the viewer of image(s) :");
  4583     cimg_help("   - CTRL+P             : Play stack of frames as a movie.");
  4584     cimg_help("   - CTRL+(mousewheel)  : Zoom in/out.");
  4585     cimg_help("   - SHIFT+(mousewheel) : Go left/right.");
  4586     cimg_help("   - ALT+(mousewheel)   : Go up/down.");
  4587     cimg_help("   - Numeric PAD        : Zoom in/out (+/-) and move zoomed region (numbers).");
  4588     cimg_help("   - BACKSPACE          : Reset zoom.\n");
  4589     cimg_help("  Specific options for the viewer of 3D object(s) :");
  4590     cimg_help("   - (mouse) + (left mouse button)   : Rotate object.");
  4591     cimg_help("   - (mouse) + (right mouse button)  : Zoom object.");
  4592     cimg_help("   - (mouse) + (middle mouse button) : Translate object.");
  4593     cimg_help("   - (mousewheel)                    : Zoom in/out.");
  4594     cimg_help("   - CTRL + Z : Enable/disable Z-buffer rendering");
  4596     cimg_help("\n  File options\n"
  4597               "  ------------");
  4598     cimg_help("  G'MIC is able to read/write most of the classical image file formats, including :");
  4599     cimg_help("   - 2D grayscale/color images : PNG,JPEG,GIF,PNM,TIFF,BMP,....");
  4600     cimg_help("   - 3D volumetric images : DICOM,HDR,NII,PAN,CIMG,INR,....");
  4601     cimg_help("   - Video files : MPEG,AVI,MOV,OGG,FLV,...");
  4602     cimg_help("   - Generic data files : DLM,ASC,RAW,TXT.");
  4603     cimg_help("   - 3D objects : OFF.\n");
  4604     cimg_help("  Specific options :");
  4605     cimg_help("   - For video files : you can read only sub-frames of the image sequence (recommended) with the expression");
  4606     cimg_help("     'video.ext,[first_frame[%][,last_frame[%][,step]]]'.");
  4607     cimg_help("   - For RAW files : you must specify the image dimensions with the expression");
  4608     cimg_help("     'file.raw,width[,height[,depth[,dim]]]]'.");
  4609     cimg_help("   - For YUV files : you must specify the image dimensions and can read only sub-frames of the image sequence with the expression");
  4610     cimg_help("     'file.yuv,width,height[,first_frame[,last_frame[,step]]]'.");
  4611     cimg_help("   - For JPEG files : you can specify the quality (in %) of an output jpeg file format with the expression");
  4612     cimg_help("     'file.jpg,30%'.");
  4614     cimg_help("\n  Examples of use\n"
  4615               "  ---------------");
  4616     cimg_help("  G'MIC is a simple but quite complete interpreter of image processing instructions, and can be used for a wide variety of");
  4617     cimg_help("  image processing tasks. Here are (very few) examples of how the command line tool G'MIC can be used :\n");
  4618     cimg_help("   - View image data : ");
  4619     cimg_help("     gmic file1.bmp file2.jpeg");
  4620     cimg_help("   - Convert image files : ");
  4621     cimg_help("     gmic input.bmp -o output.jpg");
  4622     cimg_help("   - Create volumetric image(s) from movie sequence : ");
  4623     cimg_help("     gmic input.mpg -a z -o output.hdr");
  4624     cimg_help("   - Compute image gradient norm : ");
  4625     cimg_help("     gmic input.bmp -gradient_norm");
  4626     cimg_help("   - Create G'MIC 3D logo : ");
  4627     cimg_help("     gmic 180x70x1x3 -text G\\'MIC,30,5,50,1,1 -blur 2 -n 0,100 [0] -plasma[1] \\");
  4628     cimg_help("     -+ -blur 1 -elevation -0.1 -rd3d 4");
  4629     cimg_help("\n  See also the macros defined in the provided macro file 'gmic_def.raw' for other examples.");
  4631     cimg_help("\n  ** G'MIC comes with ABSOLUTELY NO WARRANTY; "
  4632               "for details visit http://gmic.sourceforge.net **");
  4633     std::exit(0);
  4636   // Launch G'MIC instance.
  4637   //-----------------------
  4638   CImgList<float> images;
  4639   try {
  4640     gmic(argc,argv,images);
  4641   } catch (gmic_exception &e) {
  4642     std::fprintf(cimg_stdout,"\n** Error occurred : %s **\n",e.message);
  4644   return 0;
  4646 #endif
  4648 #endif // #ifdef cimg_plugin ... #else ...