Yalantis/uCrop · error · CImgDisplayException
CImgDisplay::assign(): Max number of displays (512) already…
Error message
CImgDisplay::assign(): Max number of displays (512) already opened.
What it means
CImg tracks open CImgDisplay instances in a fixed table of 512 slots (X11_attr.cimg_displays). When assign() runs and nb_cimg_displays has already reached 512, it throws CImgDisplayException because no more displays can be registered. This almost always means displays were created but never closed, leaking the table.
Solutions
- Close displays when done (call disp.close() and let it go out of scope / delete it)
- Reuse a single CImgDisplay and call disp.assign(img,...)/disp.resize() instead of constructing new ones
- Audit loops for CImgDisplay creation and ensure each iteration destroys the previous display
Example fix
// before
for (auto &img : images) { CImgDisplay d(img, "v"); while (!d.is_closed()) d.wait(); } // leaks table after 512
// after
cimg::CImgDisplay d; // reuse one display
for (auto &img : images) {
if (d.is_empty()) d.assign(img, "v"); else d.assign(img);
while (!d.is_closed() && !d.is_keyESC()) d.wait();
if (d.is_closed()) d.assign(); // reset for next iteration
} Defensive patterns
Strategy: try-catch
Validate before calling
// track your own active display count and reuse displays: if (activeDisplays >= 500) closeOldestDisplay();
Try / catch
try { d.assign(img, "win"); } catch (const cimg_library::CImgDisplayException &e) { fprintf(stderr, "Too many displays: %s\n", e.what()); d.assign(); // free a slot and retry
} Prevention
- Reuse one CImgDisplay object with assign()/resize() instead of new displays per item
- Ensure every display is closed/destroyed (RAII scopes, no leaks in loops)
- Never allocate displays per frame in animation/render loops
- Monitor nb_cimg_displays implicitly by keeping total lifetime displays per process far below 512
When it happens
Trigger: Opening more than 512 CImgDisplay objects without calling close()/the destructor — e.g. creating a new CImgDisplay per image/frame in a loop while only destroying some, or long-running apps that keep accumulating display objects.
Common situations: Image-viewer loops that spawn a window per image and rely on GC; event loops recreating displays each iteration; tools processing thousands of images each shown interactively.
Related errors
- CImgDisplay::assign(): Failed to open X11 display.
- CImgDisplay::assign(): Invalid %u bits screen mode detected…
- CImgDisplay::screen_width(): Failed to open X11 display.
- init_fullscreen(): Xrandr extension not supported by the X…
- cimg::SDL3_attr()
AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08).
Data as JSON: /api/errors/a6a9514dccf5760f.
Report an issue: GitHub.
Appendix: source
Thrown at ucrop/src/main/jni/CImg.h:10157
}
if (!_shminfo)
#endif
{
const cimg_ulong buf_size = (cimg_ulong)_width*_height*(X11_attr.nb_bits==8?1:
(X11_attr.nb_bits==16?2:4));
_data = std::malloc(buf_size);
_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),X11_attr.nb_bits,
ZPixmap,0,(char*)_data,_width,_height,8,0);
}
_wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
_wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
if (X11_attr.nb_cimg_displays>=512) {
X11_attr.unlock();
throw CImgDisplayException("CImgDisplay::assign(): Max number of displays (512) already opened.");
}
X11_attr.cimg_displays[X11_attr.nb_cimg_displays++] = this;
if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type<int>::min();
X11_attr.unlock();
cimg::mutex(14,0);
}
static int _assign_xshm(Display *dpy, XErrorEvent *error) {
cimg::unused(dpy,error);
cimg::X11_attr &X11_attr = cimg::X11_attr::ref();
X11_attr.is_shm_enabled = false;
return 0;
}
void _desinit_fullscreen() {
if (!_is_fullscreen) return;
cimg::X11_attr &X11_attr = cimg::X11_attr::ref();
Display *const dpy = X11_attr.display;View on GitHub (pinned to f788b534b4)