Mask drawing/editing renders a blank white canvas on HiDPI displays with large images (no console errors)
Actions before raising this issue
- I searched the existing issues and did not find anything similar.
- I read/searched the docs
Steps to Reproduce
Open CVAT in Chrome on a HiDPI display where
window.devicePixelRatio === 2.The issue was reproduced on a MacBook Retina display at 100% browser zoom.
Create or open a task containing a large image. The reproduced image is
12600 × 9000pixels (113.4 MP).Open the job. The background image displays normally before entering mask drawing or editing mode.
Select the brush tool, or choose Edit on an existing mask shape.
The complete canvas area becomes opaque white:
- the background image disappears;
- the mask being edited disappears;
- brush strokes are not visible;
- no relevant exception appears in the Chrome console.
Leave mask drawing/editing mode. The background image becomes visible again.
The same machine and job work when window.devicePixelRatio === 1, for example
on a non-HiDPI display or when Chrome is launched with a forced device scale
factor of 1.
Expected Behavior
The mask drawing layer should render normally above the image on HiDPI displays.
The background image and existing mask should remain visible, and brush/edit operations should work for an image that is otherwise within the browser canvas limits.
Possible Solution
MasksHandlerImpl creates its Fabric canvas without overriding Fabric's default
Retina scaling behavior:
this.canvas = new fabric.Canvas(canvas, {
containerClass: 'cvat_masks_canvas_wrapper',
fireRightClick: true,
selection: false,
defaultCursor: 'inherit',
});Fabric therefore scales the backing canvas dimensions by
window.devicePixelRatio.
For a 12600 × 9000 image at devicePixelRatio = 2, both the lower and upper
Fabric canvases attempt to use 25200 × 18000 backing stores, or 453,600,000
pixels each.
This exceeds Chromium's practical maximum 2D canvas area of 268,435,456 pixels. Chrome fails the allocation silently, so subsequent drawing operations become no-ops while the failed Fabric canvases remain above the background image.
Passing enableRetinaScaling: false when the mask editing canvas is created
prevents the backing dimensions from being multiplied by DPR:
this.canvas = new fabric.Canvas(canvas, {
containerClass: 'cvat_masks_canvas_wrapper',
fireRightClick: true,
selection: false,
defaultCursor: 'inherit',
enableRetinaScaling: false,
});With this change, the mask canvas retains the image-coordinate dimensions while
the browser continues to report window.devicePixelRatio === 2.
Context
The root-cause chain was verified in a self-hosted CVAT environment:
- Fabric enables Retina scaling by default.
- At DPR 2, the
12600 × 9000mask canvas becomes25200 × 18000. - That backing-store area is 453,600,000 pixels, above Chromium's maximum canvas area.
- Canvas allocation and drawing fail silently.
- The failed Fabric lower and upper canvases remain above the background canvas, producing an opaque white editing area.
- Exiting mask editing removes the failed overlay and makes the background image visible again.
A minimal Chrome console probe demonstrates the silent allocation failure:
const canvas = document.createElement('canvas');
canvas.width = 25200;
canvas.height = 18000;
const context = canvas.getContext('2d');
context.fillStyle = 'red';
context.fillRect(0, 0, 10, 10);
console.log(context.getImageData(2, 2, 1, 1).data);
// Returns [0, 0, 0, 0] after the oversized allocation fails silently.After applying enableRetinaScaling: false, the issue was retested on the
original HiDPI client:
- browser zoom: 100%;
window.devicePixelRatio: 2;- image size:
12600 × 9000; - lower Fabric canvas backing size:
12600 × 9000; - upper Fabric canvas backing size:
12600 × 9000; - entering brush/edit mode no longer produces a white canvas;
- mask drawing and editing work;
- annotations can be saved successfully.
The following canvas values were observed after the patch:
[
{
className: 'lower-canvas',
backingWidth: 12600,
backingHeight: 9000,
cssWidth: 12600,
cssHeight: 9000
},
{
className: 'upper-canvas',
backingWidth: 12600,
backingHeight: 9000,
cssWidth: 12600,
cssHeight: 9000
}
]This is distinct from issues where the source image itself exceeds the browser canvas limit. In this case, the source image displays normally; only the DPR-scaled mask editing canvases exceed the limit.
Environment
- CVAT: v2.71.0, self-hosted with Docker Compose
- Server OS: Ubuntu 22.04
- Client browser: Chrome 151
- Client OS: macOS
- Browser zoom: 100%
- `window.devicePixelRatio`: 2
- Reproduced image size: `12600 × 9000`
- Remote access: ngrok
- This is not relevant to the root cause because the failure occurs in the
client-side Fabric canvas.Source: cvat-ai/cvat