Yalantis/uCrop · error · CImgArgumentException

"[" cimg_appname "_math_parser] CImg<%s>: Function 'swap()':

Error message

"[" cimg_appname "_math_parser] CImg<%s>: Function 'swap()': Out-of-bounds offsets %ld and %ld (min offset: 0, max offset: %ld)."

What it means

The CImg math parser 'swap()' function exchanges two pixel values inside the input image, addressed by linear offsets. Before swapping, it validates that both offsets fall within [0, whd] (whd = width*height*depth for the vectorized per-channel variant). If either offset is out of range, a CImgArgumentException is thrown instead of performing an out-of-bounds memory access.

Source

Thrown at ucrop/src/main/jni/CImg.h:27372

      static double mp_image_swap(_cimg_math_parser& mp) {
        unsigned int ind = (unsigned int)mp.opcode[2];
        if (!mp.imglist.width()) return cimg::type<double>::nan();
        ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        CImg<T> &img = mp.imglist[ind];
        const longT
          pos0 = (longT)_mp_arg(3),
          pos1 = (longT)_mp_arg(4);
        const bool is_vector = (bool)_mp_arg(5);
        if (is_vector) {
          const longT whd = (longT)img.size()/img.spectrum();
          T *ptr0 = &img[pos0], *ptr1 = &img[pos1];
          if (pos0>=0 && pos0<=whd && pos1>=0 && pos1<=whd)
            for (unsigned int c = 0; c<img._spectrum; ++c) {
              cimg::swap(*ptr0,*ptr1);
              ptr0+=whd;
              ptr1+=whd;
            } else throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'swap()': "
                                               "Out-of-bounds offsets %ld and %ld (min offset: 0, max offset: %ld).",
                                               mp.imgin.pixel_type(),pos0,pos1,whd);
        } else {
          const longT whds = (longT)img.size();
          if (pos0>=0 && pos0<=whds && pos1>=0 && pos1<=whds)
            cimg::swap(img[pos0],img[pos1]);
          else throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'swap()': "
                                           "Out-of-bounds offsets %ld and %ld (min offset: 0, max offset: %ld).",
                                           mp.imgin.pixel_type(),pos0,pos1,whds);
        }
        return cimg::type<double>::nan();
      }

      static double mp_image_w(_cimg_math_parser& mp) {
        unsigned int ind = (unsigned int)mp.opcode[2];
        if (ind!=~0U) {
          if (!mp.imglist.width()) return cimg::type<double>::nan();
          ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());

View on GitHub (pinned to f788b534b4)

Solutions

  1. Print/inspect the image dimensions (width,height,depth,spectrum) and clamp both swap offsets to the range [0, w*h*d] before calling swap().
  2. Fix off-by-one loop bounds in the math expression so offsets never reach size() inclusive of an extra element.
  3. Use index expressions built from I[x,y,z,c]-style accessors rather than raw arithmetic offsets when possible.
  4. Wrap the expression evaluation in a try/catch for CImgArgumentException and report the offending offsets to the user.

Example fix

// before: swap(K, K + 10) with no bound check
// after in math expression:
// K < w*h*d - 10 ? swap(K, K + 10) : 0
Defensive patterns

Strategy: validation

Validate before calling

// before evaluating: check offsets against image extent
const longT whd = img.width() * img.height() * img.depth();
if (pos0 < 0 || pos0 > whd || pos1 < 0 || pos1 > whd)
  throw std::runtime_error("swap offsets out of range");

Try / catch

try { img.evaluate(expr); } catch (const CImgArgumentException& e) { log("math parser swap out of bounds: " << e.what()); }

Prevention

When it happens

Trigger: Executing a math expression like `swap(pos0,pos1)` (or the multi-channel form swap with channel loop) where pos0 or pos1 evaluates to a negative value or exceeds the image's width*height*depth bound, e.g. swap(0, w*h*d+5) on an image whose whd is smaller.

Common situations: Hand-written custom formulas that compute offsets from x,y,z,c coordinates and forget zero-based vs size-based indexing; formulas ported between images of different sizes; loop variables in math expressions running one step too far (i<=N instead of i<N).

Related errors


AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08). Data as JSON: /api/errors/0e878db1b222b8e5. Report an issue: GitHub.