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.
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)
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 s
- cimg::SDL3_attr(): %s
AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08).
Data as JSON: /api/errors/a6a9514dccf5760f.
Report an issue: GitHub.