Commit message: .
CVE: CVE-2019-13568
CWE: CWE-787
Vulnerability description: CImg through 2.6.7 has a heap-based buffer overflow in _load_bmp in CImg.h because of erroneous memory allocation for a malformed BMP image.

Project: CImg
File: CImg.h

    template<typename tfunc>
    static CImg<floatT> streamline(const tfunc& func,
                                   const float x, const float y, const float z,
                                   const float L=256, const float dl=0.1f,
                                   const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
                                   const bool is_oriented_only=false,
                                   const float x0=0, const float y0=0, const float z0=0,
                                   const float x1=0, const float y1=0, const float z1=0) {
      if (dl<=0)
        throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
                                    "(should be >0).",
                                    pixel_type(),
                                    dl);

      const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
      if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
      const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
      CImg<floatT> coordinates(size_L,3);
      const float dl2 = dl/2;
      float
        *ptr_x = coordinates.data(0,0),
        *ptr_y = coordinates.data(0,1),
        *ptr_z = coordinates.data(0,2),
        pu = (float)(dl*func(x,y,z,0)),
        pv = (float)(dl*func(x,y,z,1)),
        pw = (float)(dl*func(x,y,z,2)),
        X = x, Y = y, Z = z;

      switch (interpolation_type) {
      case 0 : { // Nearest integer interpolation
        cimg_forX(coordinates,l) {
          *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
          const int
            xi = (int)(X>0?X + 0.5f:X - 0.5f),
            yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
            zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
          float
            u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
            v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
            w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
          if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
          if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
          if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
        }
      } break;
      case 1 : { // First-order interpolation
        cimg_forX(coordinates,l) {
          *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
          float
            u = (float)(dl*func(X,Y,Z,0)),
            v = (float)(dl*func(X,Y,Z,1)),
            w = (float)(dl*func(X,Y,Z,2));
          if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
          if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
          if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
        }
      } break;
      case 2 : { // Second order interpolation
        cimg_forX(coordinates,l) {
          *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
          float
            u0 = (float)(dl2*func(X,Y,Z,0)),
            v0 = (float)(dl2*func(X,Y,Z,1)),
            w0 = (float)(dl2*func(X,Y,Z,2));
          if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
          float
            u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
            v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
            w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
          if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
          if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
          if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
        }
      } break;
      default : { // Fourth order interpolation
        cimg_forX(coordinates,k) {
          *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
          float
            u0 = (float)(dl2*func(X,Y,Z,0)),
            v0 = (float)(dl2*func(X,Y,Z,1)),
            w0 = (float)(dl2*func(X,Y,Z,2));
          if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
          float
            u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
            v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
            w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
          if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
          float
            u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
            v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
            w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
          if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
          float
            u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
            v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
            w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
          if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
          const float
            u = (u0 + u3)/3 + (u1 + u2)/1.5f,
            v = (v0 + v3)/3 + (v1 + v2)/1.5f,
            w = (w0 + w3)/3 + (w1 + w2)/1.5f;
          if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
          if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
        }
      }
      }
      if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
      return coordinates;

Project: CImg
File: CImg.h

      static bool is_nan(const unsigned int) { return false; }

Project: CImg
File: CImg.h

     **/
    CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
                              const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
                              const float gauss_prec=2, const unsigned int interpolation_type=0,
                              const bool is_fast_approx=true) {
      const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
      const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
      return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
                              amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);

Project: CImg
File: CImg.h

    template<typename t>
    CImg<T> operator<<(const CImg<t>& img) const {
      return (+*this)<<=img;

Project: CImg
File: CImg.h

    template<typename t>
    double PSNR(const CImg<t>& img, const double max_value=255) const {
      const double vMSE = (double)std::sqrt(MSE(img));
      return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());

Project: CImg
File: CImg.h

    //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
    CImg<Tuchar> get_RGBtoYCbCr() const {
      return CImg<Tuchar>(*this,false).RGBtoYCbCr();

Project: CImg
File: CImg.h


    const T& _atXYZC(const int x, const int y, const int z, const int c) const {
      return (*this)(cimg::cut(x,0,width() - 1),
                     cimg::cut(y,0,height() - 1),
                     cimg::cut(z,0,depth() - 1),
                     cimg::cut(c,0,spectrum() - 1));

Project: CImg
File: CImg.h

    template<typename t>
    CImg<t>& move_to(CImg<t>& img) {
      img.assign(*this);
      assign();
      return img;

Project: CImg
File: CImg.h

    **/
    const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
      if (!filename)
        throw CImgArgumentException(_cimg_instance
                                    "save_magick(): Specified filename is (null).",
                                    cimg_instance);
      if (is_empty()) { cimg::fempty(0,filename); return *this; }

#ifdef cimg_use_magick
      double stmin, stmax = (double)max_min(stmin);
      if (_depth>1)
        cimg::warn(_cimg_instance
                   "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
                   cimg_instance,
                   filename);

      if (_spectrum>3)
        cimg::warn(_cimg_instance
                   "save_magick(): Instance is multispectral, only the three first channels will be "
                   "saved in file '%s'.",
                   cimg_instance,
                   filename);

      if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
        cimg::warn(_cimg_instance
                   "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
                   cimg_instance,
                   filename,stmin,stmax);

      Magick::Image image(Magick::Geometry(_width,_height),"black");
      image.type(Magick::TrueColorType);
      image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
      const T
        *ptr_r = data(0,0,0,0),
        *ptr_g = _spectrum>1?data(0,0,0,1):0,
        *ptr_b = _spectrum>2?data(0,0,0,2):0;
      Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
      switch (_spectrum) {
      case 1 : // Scalar images
        for (ulongT off = (ulongT)_width*_height; off; --off) {
          pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
          ++pixels;
        }
        break;
      case 2 : // RG images
        for (ulongT off = (ulongT)_width*_height; off; --off) {
          pixels->red = (Magick::Quantum)*(ptr_r++);
          pixels->green = (Magick::Quantum)*(ptr_g++);
          pixels->blue = 0; ++pixels;
        }
        break;
      default : // RGB images
        for (ulongT off = (ulongT)_width*_height; off; --off) {
          pixels->red = (Magick::Quantum)*(ptr_r++);
          pixels->green = (Magick::Quantum)*(ptr_g++);
          pixels->blue = (Magick::Quantum)*(ptr_b++);
          ++pixels;
        }
      }
      image.syncPixels();
      image.write(filename);
      return *this;
#else
      cimg::unused(bytes_per_pixel);
      throw CImgIOException(_cimg_instance
                            "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
                            cimg_instance,
                            filename);
#endif

Project: CImg
File: CImg.h

    **/
    const CImg<T>& save_rgba(const char *const filename) const {
      return _save_rgba(0,filename);

Project: CImg
File: CImg.h

      static bool is_inf(const int) { return false; }

Project: CImg
File: CImg.h

    template<typename tp, typename tf, typename tc, typename to>
    CImg<T>& draw_object3d(LibBoard::Board& board,
                           const float x0, const float y0, const float z0,
                           const CImg<tp>& vertices, const CImgList<tf>& primitives,
                           const CImgList<tc>& colors,
                           const unsigned int render_type=4,
                           const bool is_double_sided=false, const float focale=700,
                           const float lightx=0, const float lighty=0, const float lightz=-5e8,
                           const float specular_lightness=0.2f, const float specular_shininess=0.1f,
                           const float g_opacity=1) {
      return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
                           render_type,is_double_sided,focale,lightx,lighty,lightz,
                           specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());

Project: CImg
File: CImg.h

    //! Return specified range of image columns \inplace.
    CImg<T> get_columns(const int x0, const int x1) const {
      return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);

Project: CImg
File: CImg.h

      \param pos Index of the insertion.
      \param is_shared Tells if inserted images are shared copies of images of \c list or not.
    **/
    template<typename t>
    CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
                        const bool is_shared=false) {
      if (!n) return *this;

Project: CImg
File: CImg.h

    ~CImgAbortException() throw() { delete[] _message; }
    CImgAbortException& operator=(const CImgAbortException& e) {
      const size_t size = std::strlen(e._message);
      _message = new char[size + 1];
      std::strncpy(_message,e._message,size);
      _message[size] = 0;
      return *this;

Project: CImg
File: CImg.h

      static bool is_inf(const unsigned char) { return false; }

Project: CImg
File: CImg.h

  namespace cimg {

  // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
  // (throw a CImgIOException when macro 'cimg_use_r' is defined).
    inline FILE* _stdin(const bool throw_exception) {
#ifndef cimg_use_r
      cimg::unused(throw_exception);
      return stdin;
#else
      if (throw_exception) {
        cimg::exception_mode(0);
        throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
                              "('cimg_use_r' is defined).");

Project: CImg
File: CImg.h


    CImg<T>& _priority_queue_remove(unsigned int& siz) {
      (*this)(0,0) = (*this)(--siz,0);
      (*this)(0,1) = (*this)(siz,1);
      (*this)(0,2) = (*this)(siz,2);
      (*this)(0,3) = (*this)(siz,3);
      const float value = (*this)(0,0);
      unsigned int pos = 0, swap = 0;
      do {
        const unsigned int left = 2*pos + 1, right = left + 1;
        if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
        else if (left<siz && value<(*this)(left,0)) swap = left;
        else break;
        cimg::swap((*this)(pos,0),(*this)(swap,0));
        cimg::swap((*this)(pos,1),(*this)(swap,1));
        cimg::swap((*this)(pos,2),(*this)(swap,2));
        cimg::swap((*this)(pos,3),(*this)(swap,3));
        pos = swap;
      } while (true);
      return *this;

Project: CImg
File: CImg.h

    //! Pointwise min operator between an image and an expression \newinstance.
    CImg<Tfloat> get_min(const char *const expression) const {
      return CImg<Tfloat>(*this,false).min(expression);

Project: CImg
File: CImg.h


      static double mp_sqr(_cimg_math_parser& mp) {
        return cimg::sqr(_mp_arg(2));

Project: CImg
File: CImg.h

    //! Erode image by a rectangular structuring element of specified size \newinstance.
    CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
      return (+*this).erode(sx,sy,sz);

Project: CImg
File: CImg.h


      static double mp_avg(_cimg_math_parser& mp) {
        const unsigned int i_end = (unsigned int)mp.opcode[2];
        double val = _mp_arg(3);
        for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
        return val/(i_end - 3);

Project: CImg
File: CImg.h

#endif
    }

    //! Load an image from a video file, using OpenCV library \newinstance.
    static CImgList<T> get_load_video(const char *const filename,

Project: CImg
File: CImg.h


    T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
      return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);

Project: CImg
File: CImg.h


    inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
      sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);

Project: CImg
File: CImg.h

    **/
    CImg<T>& load_bmp(const char *const filename) {
      return _load_bmp(0,filename);

Project: CImg
File: CImg.h

    **/
    const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
      return _save_pandore(file,0,colorspace);

Project: CImg
File: CImg.h

    template<typename t>
    CImg(const unsigned int size_x,
         std::initializer_list<t> values,
         const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
      assign(size_x);
      _cimg_constructor_cpp11(repeat_values);

Project: CImg
File: CImg.h

      _functor2d_int(const CImg<T>& pref):ref(pref) {}
      float operator()(const float x, const float y) const {
        return (float)ref((int)x,(int)y);

Project: CImg
File: CImg.h


      static double mp_vector_reverse(_cimg_math_parser& mp) {
        double *const ptrd = &_mp_arg(1) + 1;
        const double *const ptrs = &_mp_arg(2) + 1;
        const unsigned int p1 = (unsigned int)mp.opcode[3];
        CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
        return cimg::type<double>::nan();

Project: CImg
File: CImg.h

    //! Select sub-graph in a graph.
    CImg<intT> get_select_graph(CImgDisplay &disp,
                                const unsigned int plot_type=1, const unsigned int vertex_type=1,
                                const char *const labelx=0, const double xmin=0, const double xmax=0,
                                const char *const labely=0, const double ymin=0, const double ymax=0,
                                const bool exit_on_anykey=false) const {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "select_graph(): Empty instance.",
                                    cimg_instance);
      if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
                   set_title("CImg<%s>",pixel_type());
      const ulongT siz = (ulongT)_width*_height*_depth;
      const unsigned int old_normalization = disp.normalization();
      disp.show().set_button().set_wheel()._normalization = 0;

      double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
      if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
      if (nymin==nymax) { --nymin; ++nymax; }
      if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }

      static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
      static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };

      CImg<ucharT> colormap(3,_spectrum);
      if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
      else {
        colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
        if (_spectrum>1) { colormap(0,1) = 10;  colormap(1,1) = 220; colormap(2,1) = 10;  }
        if (_spectrum>2) { colormap(0,2) = 10;  colormap(1,2) = 10;  colormap(2,2) = 220; }
        if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10;  }
        if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10;  colormap(2,4) = 220; }
        if (_spectrum>5) { colormap(0,5) = 10;  colormap(1,5) = 220; colormap(2,5) = 220; }
        if (_spectrum>6) {
          ulongT rng = 10;
          cimg_for_inY(colormap,6,colormap.height()-1,k) {
            colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
            colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
            colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
          }
        }
      }

      CImg<ucharT> visu0, visu, graph, text, axes;
      int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
      const unsigned int one = plot_type==3?0U:1U;
      unsigned int okey = 0, obutton = 0;
      CImg<charT> message(1024);
      CImg_3x3(I,unsigned char);

      for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
        const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
        const unsigned int key = disp.key(), button = disp.button();

        // Generate graph representation.
        if (!visu0) {
          visu0.assign(disp.width(),disp.height(),1,3,220);
          const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
          if (gdimx>0 && gdimy>0) {
            graph.assign(gdimx,gdimy,1,3,255);
            if (siz<32) {
              if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
                                         false,true,black,0.2f,0x33333333,0x33333333);
            } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
            cimg_forC(*this,c)
              graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
                               plot_type,vertex_type,nymax,nymin);

            axes.assign(gdimx,gdimy,1,1,0);
            const float
              dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
              px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
              py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
            const CImg<Tdouble>
              seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
                CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
              seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);

            const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
            axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
            if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
            if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
	    if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
	    if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);

            cimg_for3x3(axes,x,y,0,0,I,unsigned char)
              if (Icc) {
                if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
                else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
              }
              else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
                cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);

            visu0.draw_image(16,16,graph);
	    visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
	      draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
          } else graph.assign();
          text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
          visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
          text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
          visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
          visu.assign();
        }

        // Generate and display current view.
        if (!visu) {
          visu.assign(visu0);
          if (graph && x0>=0 && x1>=0) {
            const int
              nx0 = x0<=x1?x0:x1,
              nx1 = x0<=x1?x1:x0,
              ny0 = y0<=y1?y0:y1,
              ny1 = y0<=y1?y1:y0,
              sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
              sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
              sy0 = 16 + ny0,
              sy1 = 16 + ny1;
            if (y0>=0 && y1>=0)
              visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
            else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
                   draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
                   draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
          }
          if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
            if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
            const unsigned int
              x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
            const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
            if (_spectrum>=7)
              cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
                            (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
                            (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
                            (double)(*this)(x,0,0,_spectrum - 1));
            else {
              cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
              cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
              cimg_sprintf(message._data + std::strlen(message),")");
            }
	    if (x0>=0 && x1>=0) {
	      const unsigned int
                nx0 = (unsigned int)(x0<=x1?x0:x1),
                nx1 = (unsigned int)(x0<=x1?x1:x0),
                ny0 = (unsigned int)(y0<=y1?y0:y1),
                ny1 = (unsigned int)(y0<=y1?y1:y0);
	      const double
                cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
                cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
                cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
                cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
	      if (y0>=0 && y1>=0)
	        cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
                             x0,cx0,cy0,x1 + one,cx1,cy1);
	      else
	        cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
                             x0,cx0,x1 + one,cx1);
	    }
            text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
            visu.draw_image((visu.width() - text.width())/2,1,~text);
          }
          visu.display(disp);
        }

        // Test keys.
        CImg<charT> filename(32);
        switch (okey = key) {
#if cimg_OS!=2
        case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
#endif
        case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
        case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
          disp.set_fullscreen(false).
            resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
                   CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
            _is_resized = true;
          disp.set_key(key,false); okey = 0;
        } break;
        case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
          disp.set_fullscreen(false).
            resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
          disp.set_key(key,false); okey = 0;
        } break;
        case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
            disp.set_fullscreen(false).
              resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
                                    CImgDisplay::screen_height()/2,1),false)._is_resized = true;
            disp.set_key(key,false); okey = 0;
          } break;
        case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
            disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
            disp.set_key(key,false); okey = 0;
          } break;
        case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
            static unsigned int snap_number = 0;
            if (visu || visu0) {
              CImg<ucharT> &screen = visu?visu:visu0;
              std::FILE *file;
              do {
                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
                if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
              } while (file);
              (+screen).__draw_text(" Saving snapshot... ",0).display(disp);
              screen.save(filename);
              (+screen).__draw_text(" Snapshot '%s' saved. ",0,filename._data).display(disp);
            }
            disp.set_key(key,false); okey = 0;
          } break;
        case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
            static unsigned int snap_number = 0;
            if (visu || visu0) {
              CImg<ucharT> &screen = visu?visu:visu0;
              std::FILE *file;
              do {

#ifdef cimg_use_zlib
                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
#else
                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
#endif
                if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
              } while (file);
              (+screen).__draw_text(" Saving instance... ",0).display(disp);
              save(filename);
              (+screen).__draw_text(" Instance '%s' saved. ",0,filename._data).display(disp);
            }
            disp.set_key(key,false); okey = 0;
          } break;
        }

        // Handle mouse motion and mouse buttons.
        if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
          visu.assign();
          if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
            const int
              mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
              cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
              my = mouse_y - 16,
              cy = cimg::cut(my,0,disp.height() - 32);
	    if (button&1) {
              if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
            }
	    else if (button&2) {
              if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
            }
            else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
          } else if (!button && obutton) selected = true;
          obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
        }
        if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
        if (visu && visu0) disp.wait();
        if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
            (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
          disp.set_key(key,false);
          okey = 0;
        }
      }

      disp._normalization = old_normalization;
      if (x1>=0 && x1<x0) cimg::swap(x0,x1);
      if (y1<y0) cimg::swap(y0,y1);
      disp.set_key(okey);
      return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);

Project: CImg
File: CImg.h

    **/
    CImgDisplay& hide_mouse() {
      return assign();

Project: CImg
File: CImg.h

    **/
    bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
      return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();

Project: CImg
File: CImg.h

    //! Dilate image by a square structuring element of specified size \newinstance.
    CImg<T> get_dilate(const unsigned int s) const {
      return (+*this).dilate(s);

Project: CImg
File: CImg.h

    //! Load image from a JPEG file \overloading.
    CImg<T>& load_jpeg(std::FILE *const file) {
      return _load_jpeg(file,0);

Project: CImg
File: CImg.h

    template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
    CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
             const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
      _width(0),_allocated_width(0),_data(0) {
      assign(6);
      _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
      _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);

Project: CImg
File: CImg.h

    template<typename t>
    CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
                                      const bool is_normalized=false, const int dilation=1) const {
      return _correlate(kernel,boundary_conditions,is_normalized,dilation,false);

Project: CImg
File: CImg.h

    //! Return a reference to an empty image \const.
    static const CImg<T>& const_empty() {
      static const CImg<T> _empty;
      return _empty;

Project: CImg
File: CImg.h

    **/
    CImgDisplay& show_mouse() {
      return assign();

Project: CImg
File: CImg.h


      static double mp_vector_off(_cimg_math_parser& mp) {
        const unsigned int
          ptr = (unsigned int)mp.opcode[2] + 1,
          siz = (unsigned int)mp.opcode[3];
        const int off = (int)_mp_arg(4);
        return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();

Project: CImg
File: CImg.h

    const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
      return _save_cimg(0,filename,is_compressed);
    }

    const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
      if (!file && !filename)
        throw CImgArgumentException(_cimglist_instance
                                    "save_cimg(): Specified filename is (null).",
                                    cimglist_instance);
#ifndef cimg_use_zlib
      if (is_compressed)
        cimg::warn(_cimglist_instance
                   "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
                   "saving them uncompressed.",
                   cimglist_instance,
                   filename?filename:"(FILE*)");
#endif
      std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
      const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
      if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
      else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
      cimglist_for(*this,l) {
        const CImg<T>& img = _data[l];
        std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
        if (img._data) {
          CImg<T> tmp;
          if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
          const CImg<T>& ref = cimg::endianness()?tmp:img;
          bool failed_to_compress = true;
          if (is_compressed) {
#ifdef cimg_use_zlib
            const ulongT siz = sizeof(T)*ref.size();
            uLongf csiz = siz + siz/100 + 16;
            Bytef *const cbuf = new Bytef[csiz];
            if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
              cimg::warn(_cimglist_instance
                         "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
                         cimglist_instance,
                         filename?filename:"(FILE*)");
            else {
              std::fprintf(nfile," #%lu\n",csiz);
              cimg::fwrite(cbuf,csiz,nfile);
              delete[] cbuf;
              failed_to_compress = false;
            }
#endif
          }
          if (failed_to_compress) { // Write in a non-compressed way
            std::fputc('\n',nfile);
            cimg::fwrite(ref._data,ref.size(),nfile);
          }
        } else std::fputc('\n',nfile);

Project: CImg
File: CImg.h

    **/
    bool is_sameN(const unsigned int size_n) const {
      return _width==size_n;

Project: CImg
File: CImg.h

    **/
    CImg<T>& load_gzip_external(const char *const filename) {
      if (!filename)
        throw CImgIOException(_cimg_instance
                              "load_gzip_external(): Specified filename is (null).",
                              cimg_instance);
      cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
      CImg<charT> command(1024), filename_tmp(256), body(256);
      const char
        *const ext = cimg::split_filename(filename,body),
        *const ext2 = cimg::split_filename(body,0);

      std::FILE *file = 0;
      do {
        if (!cimg::strcasecmp(ext,"gz")) {
          if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
          else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
                             cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
        } else {
          if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
                                  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
          else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
                             cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
        }
        if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
      } while (file);
      cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
                    cimg::gunzip_path(),
                    CImg<charT>::string(filename)._system_strescape().data(),
                    CImg<charT>::string(filename_tmp)._system_strescape().data());
      cimg::system(command);
      if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
        cimg::fclose(cimg::fopen(filename,"r"));
        throw CImgIOException(_cimg_instance
                              "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
                              cimg_instance,
                              filename);

      } else cimg::fclose(file);
      load(filename_tmp);
      std::remove(filename_tmp);
      return *this;

Project: CImg
File: CImg.h

  template<typename T>
  inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
    return img | expression;

Project: CImg
File: CImg.h

    **/
    const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
      if (!filename)
        throw CImgArgumentException(_cimg_instance
                                    "save(): Specified filename is (null).",
                                    cimg_instance);
      // Do not test for empty instances, since .cimg format is able to manage empty instances.
      const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
      const char *const ext = cimg::split_filename(filename);
      CImg<charT> nfilename(1024);
      const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
        filename;

#ifdef cimg_save_plugin
      cimg_save_plugin(fn);
#endif
#ifdef cimg_save_plugin1
      cimg_save_plugin1(fn);
#endif
#ifdef cimg_save_plugin2
      cimg_save_plugin2(fn);
#endif
#ifdef cimg_save_plugin3
      cimg_save_plugin3(fn);
#endif
#ifdef cimg_save_plugin4
      cimg_save_plugin4(fn);
#endif
#ifdef cimg_save_plugin5
      cimg_save_plugin5(fn);
#endif
#ifdef cimg_save_plugin6
      cimg_save_plugin6(fn);
#endif
#ifdef cimg_save_plugin7
      cimg_save_plugin7(fn);
#endif
#ifdef cimg_save_plugin8
      cimg_save_plugin8(fn);
#endif
      // Ascii formats
      if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
      else if (!cimg::strcasecmp(ext,"dlm") ||
               !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
      else if (!cimg::strcasecmp(ext,"cpp") ||
               !cimg::strcasecmp(ext,"hpp") ||
               !cimg::strcasecmp(ext,"h") ||
               !cimg::strcasecmp(ext,"c")) return save_cpp(fn);

      // 2D binary formats
      else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
      else if (!cimg::strcasecmp(ext,"jpg") ||
               !cimg::strcasecmp(ext,"jpeg") ||
               !cimg::strcasecmp(ext,"jpe") ||
               !cimg::strcasecmp(ext,"jfif") ||
               !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
      else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
      else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
      else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
      else if (!cimg::strcasecmp(ext,"pgm") ||
               !cimg::strcasecmp(ext,"ppm") ||
               !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
      else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
      else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
      else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
      else if (!cimg::strcasecmp(ext,"tif") ||
               !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);

      // 3D binary formats
      else if (!*ext) {
#ifdef cimg_use_zlib
        return save_cimg(fn,true);
#else
        return save_cimg(fn,false);
#endif
      } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
      else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
      else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
      else if (!cimg::strcasecmp(ext,"hdr") ||
               !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
      else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
      else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
      else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
      else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);

      // Archive files
      else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);

      // Image sequences
      else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
      else if (!cimg::strcasecmp(ext,"avi") ||
               !cimg::strcasecmp(ext,"mov") ||
               !cimg::strcasecmp(ext,"asf") ||
               !cimg::strcasecmp(ext,"divx") ||
               !cimg::strcasecmp(ext,"flv") ||
               !cimg::strcasecmp(ext,"mpg") ||
               !cimg::strcasecmp(ext,"m1v") ||
               !cimg::strcasecmp(ext,"m2v") ||
               !cimg::strcasecmp(ext,"m4v") ||
               !cimg::strcasecmp(ext,"mjp") ||
               !cimg::strcasecmp(ext,"mp4") ||
               !cimg::strcasecmp(ext,"mkv") ||
               !cimg::strcasecmp(ext,"mpe") ||
               !cimg::strcasecmp(ext,"movie") ||
               !cimg::strcasecmp(ext,"ogm") ||
               !cimg::strcasecmp(ext,"ogg") ||
               !cimg::strcasecmp(ext,"ogv") ||
               !cimg::strcasecmp(ext,"qt") ||
               !cimg::strcasecmp(ext,"rm") ||
               !cimg::strcasecmp(ext,"vob") ||
               !cimg::strcasecmp(ext,"wmv") ||
               !cimg::strcasecmp(ext,"xvid") ||
               !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
      return save_other(fn);

Project: CImg
File: CImg.h


    inline double rand(const double val_max=1) {
      cimg::mutex(4);
      const double res = cimg::rand(val_max,&cimg::rng());
      cimg::mutex(4,0);
      return res;

Project: CImg
File: CImg.h

     **/
    T& atXY(const int x, const int y, const int z=0, const int c=0) {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "atXY(): Empty instance.",
                                    cimg_instance);
      return _atXY(x,y,z,c);

Project: CImg
File: CImg.h

    template<typename tc>
    CImg<T>& draw_line(int x0, int y0,
                       int x1, int y1,
                       const tc *const color, const float opacity=1,
                       const unsigned int pattern=~0U, const bool init_hatch=true) {
      if (is_empty() || !opacity || !pattern ||
          std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
          std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;

      int
        w1 = width() - 1, h1 = height() - 1,
        dx01 = x1 - x0, dy01 = y1 - y0;

      const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
      if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
      if (pattern==~0U && y0>y1) {
        cimg::swap(x0,x1,y0,y1);
        dx01*=-1; dy01*=-1;
      }

      static unsigned int hatch = ~0U - (~0U>>1);
      if (init_hatch) hatch = ~0U - (~0U>>1);
      cimg_init_scanline(opacity);
      const int
        step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2,
        cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
      dy01+=dy01?0:1;

      for (int y = cy0; y!=cy1; y+=step) {
        const int
          yy0 = y - y0,
          x = x0 + (dx01*yy0 + hdy01)/dy01;
        if (x>=0 && x<=w1 && pattern&hatch) {
          T *const ptrd = is_horizontal?data(y,x):data(x,y);
          cimg_forC(*this,c) {
            const T val = color[c];
            ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
          }
        }
        if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
      }
      return *this;

Project: CImg
File: CImg.h

    template<typename t>
    CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const bool boundary_conditions,
                                   const bool is_normalized,
/*                                   const int x0, const int y0, const int z0,
                                   const int x1, const int y1, const int z1,
                                   const int xstep, const int ystep, const int zstep,
*/
                                   const int dilation,
                                   const bool is_convolution) const {
      if (is_empty() || !kernel || dilation<0) return *this;
      typedef _cimg_Ttfloat Ttfloat;
      CImg<Ttfloat> res;
      const ulongT
        res_whd = (ulongT)_width*_height*_depth,
        res_size = res_whd*std::max(_spectrum,kernel._spectrum);
      const bool
        is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
        is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768;
      _cimg_abort_init_omp;
      cimg_abort_init;

      if (kernel._width==kernel._height &&
          ((kernel._depth==1 && kernel._width<=5) || (kernel._depth==kernel._width && kernel._width<=3))) {

        // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel.
        if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries
          // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster.
          res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)).
            _correlate(kernel,true,is_normalized,dilation,is_convolution);
          if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2);
          else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2);

        } else { // Neumann boundaries
          res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
          cimg::unused(is_inner_parallel,is_outer_parallel);
          CImg<t> _kernel;
          if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution
            const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2);
            if (dw || dh || dd)
              kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0).
                move_to(_kernel);
          }
          if (!_kernel) _kernel = kernel.get_shared();

          switch (_kernel._depth) {
          case 3 : { // 3x3x3 kernel
            cimg_forC(res,c) {
              cimg_abort_test;
              const CImg<T> I = get_shared_channel(c%_spectrum);
              const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
              CImg<T> _res = res.get_shared_channel(c);
              if (is_normalized) {
                const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                  cimg_forXYZ(res,x,y,z) {
                  const int
                    px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                    py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                    pz = z>dilation?z - dilation:z, nz = z + dilation<res.depth()?z + dilation:z;
                  const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
                                        cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
                                        cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
                                        cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
                                        cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
                                        cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
                                        cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
                                        cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
                                        cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
                  _res(x,y,z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
                                             K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
                                             K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
                                             K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
                                             K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
                                             K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
                                             K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
                                             K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
                                             K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
                }
              } else {
                cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                  cimg_forXYZ(res,x,y,z) {
                  const int
                    px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                    py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                    pz = z>dilation?z - dilation:z, nz = z + dilation<res.depth()?z + dilation:z;
                  _res(x,y,z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
                                          K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
                                          K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
                                          K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
                                          K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
                                          K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
                                          K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
                                          K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
                                          K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
                }
              }
            }
          } break;

          case 2 : { // 2x2x2 kernel
            cimg_forC(res,c) {
              cimg_abort_test;
              const CImg<T> I = get_shared_channel(c%_spectrum);
              const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
              CImg<T> _res = res.get_shared_channel(c);
              if (is_normalized) {
                const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                  cimg_forXYZ(res,x,y,z) {
                  const int nx = x + 1<res.width()?x + 1:x, ny = y + 1<res.height()?y + 1:y,
                            nz = z + 1<res.depth()?z + 1:z;
                  const Ttfloat N = M2*(cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
                                        cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
                                        cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
                                        cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
                  _res(x,y,z) = (Ttfloat)(N?(K[0]*I(x,y,z) + K[1]*I(nx,y,z) +
                                             K[2]*I(x,ny,z) + K[3]*I(nx,ny,z) +
                                             K[4]*I(x,y,nz) + K[5]*I(nx,y,nz) +
                                             K[6]*I(x,ny,nz) + K[7]*I(nx,ny,nz))/std::sqrt(N):0);
                }
              } else {
                cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                  cimg_forXYZ(res,x,y,z) {
                  const int nx = x + 1<res.width()?x + 1:x, ny = y + 1<res.height()?y + 1:y,
                            nz = z + 1<res.depth()?z + 1:z;
                  _res(x,y,z) = (Ttfloat)(K[0]*I(x,y,z) + K[1]*I(nx,y,z) +
                                          K[2]*I(x,ny,z) + K[3]*I(nx,ny,z) +
                                          K[4]*I(x,y,nz) + K[5]*I(nx,y,nz) +
                                          K[6]*I(x,ny,nz) + K[7]*I(nx,ny,nz));
                }
              }
            }
          } break;

          default :
          case 1 :
            switch (_kernel._width) {
            case 5 : { // 5x5 kernel
              cimg_forC(res,c) {
                cimg_abort_test;
                const CImg<T> I = get_shared_channel(c%_spectrum);
                const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
                CImg<T> _res = res.get_shared_channel(c);
                if (is_normalized) {
                  const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      bx = px>dilation?px - dilation:px, ax = nx + dilation<res.width()?nx + dilation:nx,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                      by = py>dilation?py - dilation:py, ay = ny + dilation<res.height()?ny + dilation:ny;
                    const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
                                          cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
                                          cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
                                          cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
                                          cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
                                          cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
                                          cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
                                          cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
                                          cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
                                          cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
                    _res(x,y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
                                               K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
                                               K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
                                               K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
                                               K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
                                               K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
                                               K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
                                               K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
                                               K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
                                               K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
                  }
                } else {
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      bx = px>dilation?px - dilation:px, ax = nx + dilation<res.width()?nx + dilation:nx,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                      by = py>dilation?py - dilation:py, ay = ny + dilation<res.height()?ny + dilation:ny;
                    _res(x,y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
                                            K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
                                            K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
                                            K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
                                            K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
                                            K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
                                            K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
                                            K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
                                            K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
                                            K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
                  }
                }
              }
            } break;

            case 4 : { // 4x4 kernel
              cimg_forC(res,c) {
                cimg_abort_test;
                const CImg<T> I = get_shared_channel(c%_spectrum);
                const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
                CImg<T> _res = res.get_shared_channel(c);
                if (is_normalized) {
                  const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      ax = nx + dilation<res.width()?nx + dilation:nx,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                      ay = ny + dilation<res.height()?ny + dilation:ny;
                    const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
                                          cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
                                          cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
                                          cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
                                          cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
                                          cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
                                          cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
                                          cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
                    _res(x,y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) +
                                               K[2]*I(nx,py,z) + K[3]*I(ax,py,z) +
                                               K[4]*I(px,y,z) + K[5]*I(x,y,z) +
                                               K[6]*I(nx,y,z) + K[7]*I(ax,y,z) +
                                               K[8]*I(px,ny,z) + K[9]*I(x,ny,z) +
                                               K[10]*I(nx,ny,z) + K[11]*I(ax,ny,z) +
                                               K[12]*I(px,ay,z) + K[13]*I(x,ay,z) +
                                               K[14]*I(nx,ay,z) + K[15]*I(ax,ay,z))/std::sqrt(N):0);
                  }
                } else {
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      ax = nx + dilation<res.width()?nx + dilation:nx,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y,
                      ay = ny + dilation<res.height()?ny + dilation:ny;
                    _res(x,y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) +
                                            K[2]*I(nx,py,z) + K[3]*I(ax,py,z) +
                                            K[4]*I(px,y,z) + K[5]*I(x,y,z) +
                                            K[6]*I(nx,y,z) + K[7]*I(ax,y,z) +
                                            K[8]*I(px,ny,z) + K[9]*I(x,ny,z) +
                                            K[10]*I(nx,ny,z) + K[11]*I(ax,ny,z) +
                                            K[12]*I(px,ay,z) + K[13]*I(x,ay,z) +
                                            K[14]*I(nx,ay,z) + K[15]*I(ax,ay,z));
                  }
                }
              }
            } break;

            case 3 : { // 3x3 kernel
              cimg_forC(res,c) {
                cimg_abort_test;
                const CImg<T> I = get_shared_channel(c%_spectrum);
                const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
                CImg<T> _res = res.get_shared_channel(c);
                if (is_normalized) {
                  const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y;
                    const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
                                          cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
                                          cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
                    _res(x,y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
                                               K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
                                               K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
                  }
                } else {
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int
                      px = x>dilation?x - dilation:x, nx = x + dilation<res.width()?x + dilation:x,
                      py = y>dilation?y - dilation:y, ny = y + dilation<res.height()?y + dilation:y;
                    _res(x,y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
                                            K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
                                            K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
                  }
                }
              }
            } break;

            case 2 : { // 2x2 kernel
              cimg_forC(res,c) {
                cimg_abort_test;
                const CImg<T> I = get_shared_channel(c%_spectrum);
                const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
                CImg<T> _res = res.get_shared_channel(c);
                if (is_normalized) {
                  const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int nx = x + 1<res.width()?x + 1:x, ny = y + 1<res.height()?y + 1:y;
                    const Ttfloat N = M2*(cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
                                          cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
                    _res(x,y,z) = (Ttfloat)(N?(K[0]*I(x,y,z) + K[1]*I(nx,y,z) +
                                               K[2]*I(x,ny,z) + K[3]*I(nx,ny,z))/std::sqrt(N):0);
                  }
                } else {
                  cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
                    cimg_forXYZ(res,x,y,z) {
                    const int nx = x + 1<res.width()?x + 1:x, ny = y + 1<res.height()?y + 1:y;
                    _res(x,y,z) = (Ttfloat)(K[0]*I(x,y,z) + K[1]*I(nx,y,z) +
                                            K[2]*I(x,ny,z) + K[3]*I(nx,ny,z));
                  }
                }
              }
            } break;

            default :
            case 1 : // 1x1 kernel
              if (is_normalized) res.fill(1);
              else cimg_forC(res,c) {
                  cimg_abort_test;
                  const CImg<T> img = get_shared_channel(c%_spectrum);
                  res.get_shared_channel(c).assign(img)*=_kernel(0,0,0,c%kernel._spectrum);
                }
              break;
            }
          }
        }
      } else { // Generic version for other kernels and boundary conditions
        res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
        int
          mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
          mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1;
        if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution
        const int
          mxs = dilation*mx1, mys = dilation*my1, mzs = dilation*mz1,
          mxe = width() - dilation*mx2, mye = height() - dilation*my2, mze = depth() - dilation*mz2;
        cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
        cimg_forC(res,c) _cimg_abort_try_omp {
          cimg_abort_test;
          const CImg<T> img = get_shared_channel(c%_spectrum);
          const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
          if (is_normalized) { // Normalized correlation
            const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
            cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
            for (int z = mzs; z<mze; ++z)
              for (int y = mys; y<mye; ++y)
                for (int x = mxs; x<mxe; ++x) _cimg_abort_try_omp2 {
                  cimg_abort_test2;
                  Ttfloat val = 0, N = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm) {
                        const Ttfloat _val = (Ttfloat)img(x + dilation*xm,y + dilation*ym,z + dilation*zm);
                        val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
                        N+=_val*_val;
                      }
                  N*=M2;
                  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
                } _cimg_abort_catch_omp2
            if (boundary_conditions)
              cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
              cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
                cimg_abort_test2;
                for (int x = 0; x<width();
                     (y<mys || y>=mye || z<mzs || z>=mze)?++x:((x<mxs - 1 || x>=mxe)?++x:(x=mxe))) {
                  Ttfloat val = 0, N = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm) {
                        const Ttfloat _val = (Ttfloat)img._atXYZ(x + dilation*xm,y + dilation*ym,z + dilation*zm);
                        val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
                        N+=_val*_val;
                      }
                  N*=M2;
                  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
                }
              } _cimg_abort_catch_omp2
            else
              cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
              cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
                cimg_abort_test2;
                for (int x = 0; x<width();
                     (y<mys || y>=mye || z<mzs || z>=mze)?++x:((x<mxs - 1 || x>=mxe)?++x:(x=mxe))) {
                  Ttfloat val = 0, N = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm) {
                        const Ttfloat _val = (Ttfloat)img.atXYZ(x + dilation*xm,y + dilation*ym,z + dilation*zm,0,(T)0);
                        val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
                        N+=_val*_val;
                      }
                  N*=M2;
                  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
                }
              } _cimg_abort_catch_omp2
          } else { // Classical correlation
            cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
            for (int z = mzs; z<mze; ++z)
              for (int y = mys; y<mye; ++y)
                for (int x = mxs; x<mxe; ++x) _cimg_abort_try_omp2 {
                  cimg_abort_test2;
                  Ttfloat val = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm)
                        val+=img(x + dilation*xm,y + dilation*ym,z + dilation*zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
                  res(x,y,z,c) = (Ttfloat)val;
                } _cimg_abort_catch_omp2
            if (boundary_conditions)
              cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
              cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
                cimg_abort_test2;
                for (int x = 0; x<width();
                     (y<mys || y>=mye || z<mzs || z>=mze)?++x:((x<mxs - 1 || x>=mxe)?++x:(x=mxe))) {
                  Ttfloat val = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm)
                        val+=img._atXYZ(x + dilation*xm,y + dilation*ym,z + dilation*zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
                  res(x,y,z,c) = (Ttfloat)val;
                }
              } _cimg_abort_catch_omp2
            else
              cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
              cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
                cimg_abort_test2;
                for (int x = 0; x<width();
                     (y<mys || y>=mye || z<mzs || z>=mze)?++x:((x<mxs - 1 || x>=mxe)?++x:(x=mxe))) {
                  Ttfloat val = 0;
                  for (int zm = -mz1; zm<=mz2; ++zm)
                    for (int ym = -my1; ym<=my2; ++ym)
                      for (int xm = -mx1; xm<=mx2; ++xm)
                        val+=img.atXYZ(x + dilation*xm,y + dilation*ym,z + dilation*zm,0,(T)0)*
                          K(mx1 + xm,my1 + ym,mz1 + zm);
                  res(x,y,z,c) = (Ttfloat)val;
                }
              } _cimg_abort_catch_omp2
          }
        } _cimg_abort_catch_omp
      }
      cimg_abort_test;
      return res;

Project: CImg
File: CImg.h


    Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
      const float
        nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,(float)_width),
        nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,(float)_height);
      const int x = (int)nfx, y = (int)nfy;
      const float dx = nfx - x, dy = nfy - y;
      const int
        px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
        py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height());
      const Tfloat
        Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
        Iap = (Tfloat)(*this)(ax,py,z,c),
        Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)),
        Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
        Iac = (Tfloat)(*this)(ax,y,z,c),
        Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)),
        Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
        Ian = (Tfloat)(*this)(ax,ny,z,c),
        In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)),
        Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
        Iaa = (Tfloat)(*this)(ax,ay,z,c),
        Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa));
      return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia));

Project: CImg
File: CImg.h

  template<typename T>
  inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
    return img & expression;

Project: CImg
File: CImg.h

    **/
    CImg<T> rotate(const float u, const float v, const float w, const float angle,
                   const unsigned int interpolation, const unsigned int boundary_conditions) {
      const float nangle = cimg::mod(angle,360.f);
      if (nangle==0.f) return *this;
      return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);

Project: CImg
File: CImg.h

    //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
    const T& at(const int offset) const {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "at(): Empty instance.",
                                    cimg_instance);
      return _at(offset);

Project: CImg
File: CImg.h

       \param dy Checked image height.
       \param dz Checked image depth.
       \param dc Checked image spectrum.
    **/
    bool is_sameNXYZC(const unsigned int n,

Project: CImg
File: CImg.h

    //! Return reference to the first image of the list \const.
    const CImg<T>& front() const {
      return *_data;

Project: CImg
File: CImg.h

      static const char* format_s() { return "%d"; }

Project: CImg
File: CImg.h

      // Return true if specified argument can be a part of an allowed  variable name.
      bool is_varchar(const char c) const {
        return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';

Project: CImg
File: CImg.h

    **/
    CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) {
      const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
      return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);

Project: CImg
File: CImg.h

    **/
    CImg<T>& identity_matrix() {
      return identity_matrix(std::max(_width,_height)).move_to(*this);

Project: CImg
File: CImg.h

    //! Save image as a RGB file \overloading.
    const CImg<T>& save_rgb(std::FILE *const file) const {
      return _save_rgb(file,0);

Project: CImg
File: CImg.h

      static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }

Project: CImg
File: CImg.h

    template<> struct type<half> {

Project: CImg
File: CImg.h

    template<typename t>
    CImg<T>& blur_anisotropic(const CImg<t>& G,
                              const float amplitude=60, const float dl=0.8f, const float da=30,
                              const float gauss_prec=2, const unsigned int interpolation_type=0,
                              const bool is_fast_approx=1) {

      // Check arguments and init variables
      if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
        throw CImgArgumentException(_cimg_instance
                                    "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
                                    cimg_instance,
                                    G._width,G._height,G._depth,G._spectrum,G._data);
      if (is_empty() || dl<0) return *this;
      const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
      unsigned int iamplitude = cimg::round(namplitude);
      const bool is_3d = (G._spectrum==6);
      T val_min, val_max = max_min(val_min);
      _cimg_abort_init_omp;
      cimg_abort_init;

      if (da<=0) {  // Iterated oriented Laplacians
        CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
        for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
          Tfloat *ptrd = velocity._data, veloc_max = 0;
          if (is_3d) // 3D version
            cimg_forC(*this,c) {
              cimg_abort_test;
              CImg_3x3x3(I,Tfloat);
              cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
                const Tfloat
                  ixx = Incc + Ipcc - 2*Iccc,
                  ixy = (Innc + Ippc - Inpc - Ipnc)/4,
                  ixz = (Incn + Ipcp - Incp - Ipcn)/4,
                  iyy = Icnc + Icpc - 2*Iccc,
                  iyz = (Icnn + Icpp - Icnp - Icpn)/4,
                  izz = Iccn + Iccp - 2*Iccc,
                  veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
                                   G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
                *(ptrd++) = veloc;
                if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
              }
            }
          else // 2D version
            cimg_forZC(*this,z,c) {
              cimg_abort_test;
              CImg_3x3(I,Tfloat);
              cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
                const Tfloat
                  ixx = Inc + Ipc - 2*Icc,
                  ixy = (Inn + Ipp - Inp - Ipn)/4,
                  iyy = Icn + Icp - 2*Icc,
                  veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
                *(ptrd++) = veloc;
                if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
              }
            }
          if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
        }
      } else { // LIC-based smoothing
        const ulongT whd = (ulongT)_width*_height*_depth;
        const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
        const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
        CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
        int N = 0;
        if (is_3d) { // 3D version
          for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
            const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
              da2 = datmp<1?360.f:datmp;
            for (float theta = 0; theta<360; (theta+=da2),++N) {
              const float
                thetar = (float)(theta*cimg::PI/180),
                vx = (float)(std::cos(thetar)*std::cos(phir)),
                vy = (float)(std::sin(thetar)*std::cos(phir)),
                vz = (float)std::sin(phir);
              const t
                *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
                *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
              Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3);
              cimg_forXYZ(G,xg,yg,zg) {
                const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
                const float
                  u = (float)(a*vx + b*vy + c*vz),
                  v = (float)(b*vx + d*vy + e*vz),
                  w = (float)(c*vx + e*vy + f*vz),
                  n = 1e-5f + cimg::hypot(u,v,w),
                  dln = dl/n;
                *(pd0++) = (Tfloat)(u*dln);
                *(pd1++) = (Tfloat)(v*dln);
                *(pd2++) = (Tfloat)(w*dln);
                *(pd3++) = (Tfloat)n;
              }

              cimg_abort_test;
              cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
                                 cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
                                 firstprivate(val))
              cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
                cimg_abort_test2;
                cimg_forX(*this,x) {
                  val.fill(0);
                  const float
                    n = (float)W(x,y,z,3),
                    fsigma = (float)(n*sqrt2amplitude),
                    fsigma2 = 2*fsigma*fsigma,
                    length = gauss_prec*fsigma;
                  float
                    S = 0,
                    X = (float)x,
                    Y = (float)y,
                    Z = (float)z;
                  switch (interpolation_type) {
                  case 0 : { // Nearest neighbor
                    for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
                      const int
                        cx = (int)(X + 0.5f),
                        cy = (int)(Y + 0.5f),
                        cz = (int)(Z + 0.5f);
                      const float
                        u = (float)W(cx,cy,cz,0),
                        v = (float)W(cx,cy,cz,1),
                        w = (float)W(cx,cy,cz,2);
                      if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
                      else {
                        const float coef = (float)std::exp(-l*l/fsigma2);
                        cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
                        S+=coef;
                      }
                      X+=u; Y+=v; Z+=w;
                    }
                  } break;
                  case 1 : { // Linear interpolation
                    for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
                      const float
                        u = (float)(W._linear_atXYZ(X,Y,Z,0)),
                        v = (float)(W._linear_atXYZ(X,Y,Z,1)),
                        w = (float)(W._linear_atXYZ(X,Y,Z,2));
                      if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
                      else {
                        const float coef = (float)std::exp(-l*l/fsigma2);
                        cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
                        S+=coef;
                      }
                      X+=u; Y+=v; Z+=w;
                    }
                  } break;
                  default : { // 2nd order Runge Kutta
                    for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
                      const float
                        u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
                        v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
                        w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
                        u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
                        v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
                        w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
                      if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
                      else {
                        const float coef = (float)std::exp(-l*l/fsigma2);
                        cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
                        S+=coef;
                      }
                      X+=u; Y+=v; Z+=w;
                    }
                  } break;
                  }
                  Tfloat *ptrd = res.data(x,y,z);
                  if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
                  else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
                }
              } _cimg_abort_catch_omp2
            }
          }
        } else { // 2D LIC algorithm
          for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
            const float thetar = (float)(theta*cimg::PI/180),
              vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
            const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
            Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
            cimg_forXY(G,xg,yg) {
              const t a = *(pa++), b = *(pb++), c = *(pc++);
              const float
                u = (float)(a*vx + b*vy),
                v = (float)(b*vx + c*vy),
                n = std::max(1e-5f,cimg::hypot(u,v)),
                dln = dl/n;
              *(pd0++) = (Tfloat)(u*dln);
              *(pd1++) = (Tfloat)(v*dln);
              *(pd2++) = (Tfloat)n;
            }

            cimg_abort_test;
            cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
                               firstprivate(val))
            cimg_forY(*this,y) _cimg_abort_try_omp2 {
              cimg_abort_test2;
              cimg_forX(*this,x) {
                val.fill(0);
                const float
                  n = (float)W(x,y,0,2),
                  fsigma = (float)(n*sqrt2amplitude),
                  fsigma2 = 2*fsigma*fsigma,
                  length = gauss_prec*fsigma;
                float
                  S = 0,
                  X = (float)x,
                  Y = (float)y;
                switch (interpolation_type) {
                case 0 : { // Nearest-neighbor
                  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
                    const int
                      cx = (int)(X + 0.5f),
                      cy = (int)(Y + 0.5f);
                    const float
                      u = (float)W(cx,cy,0,0),
                      v = (float)W(cx,cy,0,1);
                    if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
                    else {
                      const float coef = (float)std::exp(-l*l/fsigma2);
                      cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
                      S+=coef;
                    }
                    X+=u; Y+=v;
                  }
                } break;
                case 1 : { // Linear interpolation
                  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
                    const float
                      u = (float)(W._linear_atXY(X,Y,0,0)),
                      v = (float)(W._linear_atXY(X,Y,0,1));
                    if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
                    else {
                      const float coef = (float)std::exp(-l*l/fsigma2);
                      cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
                      S+=coef;
                    }
                    X+=u; Y+=v;
                  }
                } break;
                default : { // 2nd-order Runge-kutta interpolation
                  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
                    const float
                      u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
                      v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
                      u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
                      v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
                    if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
                    else {
                      const float coef = (float)std::exp(-l*l/fsigma2);
                      cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
                      S+=coef;
                    }
                    X+=u; Y+=v;
                  }
                }
                }
                Tfloat *ptrd = res.data(x,y);
                if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
                else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
              }
            } _cimg_abort_catch_omp2
          }
        }
        const Tfloat *ptrs = res._data;
        cimg_for(*this,ptrd,T) {
          const Tfloat _val = *(ptrs++)/N;
          *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
        }
      }
      cimg_abort_test;
      return *this;

Project: CImg
File: CImg.h


      unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
        const unsigned int
          siz = _cimg_mp_size(arg2),
          pos = is_comp_vector(arg2)?arg2:vector(siz);
        if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
        else {
          code.insert(siz);
          for (unsigned int k = 1; k<=siz; ++k)
            CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
        }
        return pos;

Project: CImg
File: CImg.h

    template<typename t>
    CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
                        const bool is_increasing, const bool is_permutations) {
      if (indm<indM) {
        const long mid = (indm + indM)/2;
        if (is_increasing) {
          if ((*this)[indm]>(*this)[mid]) {
            cimg::swap((*this)[indm],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
          }
          if ((*this)[mid]>(*this)[indM]) {
            cimg::swap((*this)[indM],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
          }
          if ((*this)[indm]>(*this)[mid]) {
            cimg::swap((*this)[indm],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
          }
        } else {
          if ((*this)[indm]<(*this)[mid]) {
            cimg::swap((*this)[indm],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
          }
          if ((*this)[mid]<(*this)[indM]) {
            cimg::swap((*this)[indM],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
          }
          if ((*this)[indm]<(*this)[mid]) {
            cimg::swap((*this)[indm],(*this)[mid]);
            if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
          }
        }
        if (indM - indm>=3) {
          const T pivot = (*this)[mid];
          long i = indm, j = indM;
          if (is_increasing) {
            do {
              while ((*this)[i]<pivot) ++i;
              while ((*this)[j]>pivot) --j;
              if (i<=j) {
                if (is_permutations) cimg::swap(permutations[i],permutations[j]);
                cimg::swap((*this)[i++],(*this)[j--]);
              }
            } while (i<=j);
          } else {
            do {
              while ((*this)[i]>pivot) ++i;
              while ((*this)[j]<pivot) --j;
              if (i<=j) {
                if (is_permutations) cimg::swap(permutations[i],permutations[j]);
                cimg::swap((*this)[i++],(*this)[j--]);
              }
            } while (i<=j);
          }
          if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
          if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
        }
      }
      return *this;

Project: CImg
File: CImg.h

    //! Return stream line of a 3D vector field \overloading.
    static CImg<floatT> streamline(const char *const expression,
                                   const float x, const float y, const float z,
                                   const float L=256, const float dl=0.1f,
                                   const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
                                   const bool is_oriented_only=false,
                                   const float x0=0, const float y0=0, const float z0=0,
                                   const float x1=0, const float y1=0, const float z1=0) {
      _functor4d_streamline_expr func(expression);
      return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);

Project: CImg
File: CImg.h

      }
      float operator()(const float x, const float y) const {
        return (float)(*mp)(x,y,0,0);

Project: CImg
File: CImg.h

    //! Fill all pixel values with specified value \newinstance.
    CImg<T> get_fill(const T& val) const {
      return CImg<T>(_width,_height,_depth,_spectrum).fill(val);

Project: CImg
File: CImg.h

    template<typename t>
    CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
      if (is_shared) {
        _width = _height = _depth = _spectrum = 0; _data = 0;
        throw CImgArgumentException(_cimg_instance
                                    "CImg(): Invalid construction request of a shared instance from a "
                                    "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
                                    cimg_instance,
                                    CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
      }
      const size_t siz = (size_t)img.size();
      if (img._data && siz) {
        _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
        try { _data = new T[siz]; } catch (...) {
          _width = _height = _depth = _spectrum = 0; _data = 0;
          throw CImgInstanceException(_cimg_instance
                                      "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
                                      cimg_instance,
                                      cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
                                      img._width,img._height,img._depth,img._spectrum);
        }
        const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
      } else { _width = _height = _depth = _spectrum = 0; _data = 0; }

Project: CImg
File: CImg.h


      static double mp_factorial(_cimg_math_parser& mp) {
        return cimg::factorial((int)_mp_arg(2));

Project: CImg
File: CImg.h

    inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
      va_list ap;
      va_start(ap,format);
      const int result = _vsnprintf(s,size,format,ap);
      va_end(ap);
      return result;
    }

Project: CImg
File: CImg.h

    **/
    CImg<T>& permute_axes(const char *const order) {
      return get_permute_axes(order).move_to(*this);

Project: CImg
File: CImg.h

    **/
    CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
      return get_shared_rows(y0,y0,z0,c0);

Project: CImg
File: CImg.h

    template<typename tz, typename tc, typename tl>
    CImg<T>& draw_triangle(CImg<tz>& zbuffer,
                           int x0, int y0, const float z0,
                           int x1, int y1, const float z1,
                           int x2, int y2, const float z2,
                           const tc *const color,
                           const CImg<tl>& light,
                           int lx0, int ly0,
                           int lx1, int ly1,
                           int lx2, int ly2,
                           const float opacity=1) {
      if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
      if (!color)
        throw CImgArgumentException(_cimg_instance
                                    "draw_triangle(): Specified color is (null).",
                                    cimg_instance);
      if (light._depth>1 || light._spectrum<_spectrum)
        throw CImgArgumentException(_cimg_instance
                                    "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
                                    cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
      if (!is_sameXY(zbuffer))
        throw CImgArgumentException(_cimg_instance
                                    "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
                                    "different dimensions.",
                                    cimg_instance,
                                    zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
      if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
                                                     +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);

      float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
      if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
      if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
      if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
      if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;

      const int
        w1 = width() - 1, h1 = height() - 1,
        dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
        dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
        cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
        hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
        dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
        dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
        hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
        hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
      const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;

      const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
      cimg_init_scanline(opacity);

      for (int y = cy0; y<=cy2; ++y) {
        const int yy0 = y - y0, yy1 = y - y1;
        int
          xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
          xM = x0 + (dx02*yy0 + hdy02)/dy02,
          lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
          lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
          lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
          lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
        float
          izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
          izM = iz0 + diz02*yy0/dy02;

        if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
        if (xM>=0 || xm<=w1) {
          const int
            cxm = cimg::cut(xm,0,w1),
            cxM = cimg::cut(xM,0,w1);
          T *ptrd = data(cxm,y);
          tz *ptrz = zbuffer.data(cxm,y);
          const int
            dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
            dlxmM = lxM - lxm, dlymM = lyM - lym;
          const float dizmM = izM - izm;

          for (int x = cxm; x<cxM; ++x) {
            const int xxm = x - xm;
            const float iz = izm + dizmM*xxm/dxmM;
            if (iz>=*ptrz) {
              *ptrz = (tz)iz;
              const int
                lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
                ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
              const tl *const lig = &light._atXY(lx,ly);
              cimg_forC(*this,c) {
                const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
                const tc col = color[c];
                const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
                ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
              }
            }
            ++ptrd; ++ptrz;
          }
        }
      }
      return *this;

Project: CImg
File: CImg.h

    template<typename T>
    CImgDisplay& assign(const CImg<T>& img,
                        const char *const title=0, const unsigned int normalization=3,
                        const bool is_fullscreen=false, const bool is_closed=false) {
      _no_display_exception();
      return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);

Project: CImg
File: CImg.h

    }
    inline int mod(const unsigned char x, const unsigned char m) {
      return x%m;

Project: CImg
File: CImg.h

    **/
    CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
      if (is_empty()) return *this;
      CImg<uintT> perm;
      switch (cimg::lowercase(axis)) {
      case 0 :
        _quicksort(0,size() - 1,perm,is_increasing,false);
        break;
      case 'x' : {
        perm.assign(_width);
        get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
        CImg<T> img(*this,false);
        cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
      } break;
      case 'y' : {
        perm.assign(_height);
        get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
        CImg<T> img(*this,false);
        cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
      } break;
      case 'z' : {
        perm.assign(_depth);
        get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
        CImg<T> img(*this,false);
        cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
      } break;
      case 'c' : {
        perm.assign(_spectrum);
        get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
        CImg<T> img(*this,false);
        cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
      } break;
      default :
        throw CImgArgumentException(_cimg_instance
                                    "sort(): Invalid specified axis '%c' "
                                    "(should be { x | y | z | c }).",
                                    cimg_instance,axis);
      }
      return *this;

Project: CImg
File: CImg.h

     **/
    CImg<T>& resize(const CImgDisplay& disp,
                    const int interpolation_type=1, const unsigned int boundary_conditions=0,
                    const float centering_x = 0, const float centering_y = 0,
                    const float centering_z = 0, const float centering_c = 0) {
      return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
                    centering_x,centering_y,centering_z,centering_c);

Project: CImg
File: CImg.h

    template<typename t>
    CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
      return CImg<Tfloat>(*this,false).distance(value,metric_mask);

Project: CImg
File: CImg.h

      static bool is_inf(const unsigned short) { return false; }

Project: CImg
File: CImg.h


    const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
      if (!file && !filename)
        throw CImgArgumentException(_cimg_instance
                                    "save_rgba(): Specified filename is (null).",
                                    cimg_instance);
      if (is_empty()) { cimg::fempty(file,filename); return *this; }
      if (_spectrum!=4)
        cimg::warn(_cimg_instance
                   "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
                   cimg_instance,
                   filename?filename:"(FILE*)");

      std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
      const ulongT wh = (ulongT)_width*_height;
      unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
      const T
        *ptr1 = data(0,0,0,0),
        *ptr2 = _spectrum>1?data(0,0,0,1):0,
        *ptr3 = _spectrum>2?data(0,0,0,2):0,
        *ptr4 = _spectrum>3?data(0,0,0,3):0;
      switch (_spectrum) {
      case 1 : { // Scalar images
        for (ulongT k = 0; k<wh; ++k) {
          const unsigned char val = (unsigned char)*(ptr1++);
          *(nbuffer++) = val;
          *(nbuffer++) = val;
          *(nbuffer++) = val;
          *(nbuffer++) = 255;
        }
      } break;
      case 2 : { // RG images
        for (ulongT k = 0; k<wh; ++k) {
          *(nbuffer++) = (unsigned char)(*(ptr1++));
          *(nbuffer++) = (unsigned char)(*(ptr2++));
          *(nbuffer++) = 0;
          *(nbuffer++) = 255;
        }
      } break;
      case 3 : { // RGB images
        for (ulongT k = 0; k<wh; ++k) {
          *(nbuffer++) = (unsigned char)(*(ptr1++));
          *(nbuffer++) = (unsigned char)(*(ptr2++));
          *(nbuffer++) = (unsigned char)(*(ptr3++));
          *(nbuffer++) = 255;
        }
      } break;
      default : { // RGBA images
        for (ulongT k = 0; k<wh; ++k) {
          *(nbuffer++) = (unsigned char)(*(ptr1++));
          *(nbuffer++) = (unsigned char)(*(ptr2++));
          *(nbuffer++) = (unsigned char)(*(ptr3++));
          *(nbuffer++) = (unsigned char)(*(ptr4++));
        }
      }
      }
      cimg::fwrite(buffer,4*wh,nfile);
      if (!file) cimg::fclose(nfile);
      delete[] buffer;
      return *this;

Project: CImg
File: CImg.h

    **/
    CImg<T>& load_exr(const char *const filename) {
      if (!filename)
        throw CImgArgumentException(_cimg_instance
                                    "load_exr(): Specified filename is (null).",
                                    cimg_instance);
#if defined(cimg_use_openexr)
      Imf::RgbaInputFile file(filename);
      Imath::Box2i dw = file.dataWindow();
      const int
        inwidth = dw.max.x - dw.min.x + 1,
        inheight = dw.max.y - dw.min.y + 1;
      Imf::Array2D<Imf::Rgba> pixels;
      pixels.resizeErase(inheight,inwidth);
      file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
      file.readPixels(dw.min.y, dw.max.y);
      assign(inwidth,inheight,1,4);
      T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
      cimg_forXY(*this,x,y) {
        *(ptr_r++) = (T)pixels[y][x].r;
        *(ptr_g++) = (T)pixels[y][x].g;
        *(ptr_b++) = (T)pixels[y][x].b;
        *(ptr_a++) = (T)pixels[y][x].a;
      }
#elif defined(cimg_use_tinyexr)
      float *res;
      const char *err = 0;
      int width = 0, height = 0;
      const int ret = LoadEXR(&res,&width,&height,filename,&err);
      if (ret) throw CImgIOException(_cimg_instance
                                     "load_exr(): Unable to load EXR file '%s'.",
                                     cimg_instance,filename);
      CImg<floatT>(out,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
      std::free(res);
#else
      return load_other(filename);
#endif
      return *this;

Project: CImg
File: CImg.h

    **/
    CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
                    const unsigned int boundary_conditions=0) {
      const float nangle = cimg::mod(angle,360.f);
      if (nangle==0.f) return *this;
      return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);

Project: CImg
File: CImg.h


      static double mp_round(_cimg_math_parser& mp) {
        return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));

Project: CImg
File: CImg.h

      }
      static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
        if (sizeof(half)==2) {
          short u;
          std::memcpy(&u,&val,sizeof(short));
          return (bool)((u&0x7fff)>0x7c00);
        }
        return cimg::type<float>::is_nan((float)val);