DEV Community

David Miller
David Miller

Posted on

Check an image's pixel count before allocating a canvas

Before assigning an image's width and height to a canvas, calculate their product. A file picker shows the compressed file size, while the drawing operation needs space for the decoded pixels.

Consider a hypothetical image measuring 8000 by 6000 pixels. It contains 48 million pixels. In an ordinary eight-bit RGBA buffer, four bytes per pixel would occupy 192 million bytes, or about 183 MiB. That calculation describes one buffer. It does not measure the browser's total memory use, which can include a decoded source and temporary allocations.

Choose a limit for the operation

For a small preview, a 48-million-pixel drawing surface is often unnecessary. Decide how large the preview needs to be, then calculate a destination that preserves the source aspect ratio. If you also limit source dimensions, make that a separate policy so that a small preview is not mistaken for a cheap source decode.

The following function checks dimensions already obtained from a decoder. Its numbers are example application policies, not browser limits. It does not inspect a compressed file or stop the initial decoding allocation.

function previewSize(width, height) {
  const maxSourcePixels = 24_000_000;
  const maxPreviewEdge = 1600;
  if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) ||
      width < 1 || height < 1 || width * height > maxSourcePixels) {
    throw new Error("Image dimensions exceed this preview policy");
  }
  const scale = Math.min(1, maxPreviewEdge / Math.max(width, height));
  return {
    width: Math.max(1, Math.round(width * scale)),
    height: Math.max(1, Math.round(height * scale))
  };
}
Enter fullscreen mode Exit fullscreen mode

Call this before assigning the canvas dimensions. A 6000 by 4000 image passes this example's source policy and produces a 1600 by 1067 preview. Rounding can change the exact ratio by a fraction of a pixel. If the application requires exact output dimensions, define that requirement separately.

Put the check at the right boundary

There are two different allocation boundaries here. The decoder may have allocated the source image before it exposes dimensions. The canvas assignment then requests a drawing buffer for the destination. Checking dimensions between those operations can prevent an unnecessarily large destination, but it cannot undo memory already used to decode the source.

Applications accepting untrusted images need appropriate controls on their decoding path too. A front-end check is useful feedback for ordinary users; it is not a security guarantee for a server that later processes the same upload. Avoid describing this helper as protection against every oversized or malicious input.

A batch interface adds another decision: how many images can remain active together? Even a reasonable per-image budget becomes expensive when several sources and previews overlap. Start with a bounded queue, release resources when their consumers finish, and measure on the devices the interface supports. There is no single safe memory allowance that applies to every browser and phone.

I work on Morphio, an image conversion project. This distinction belongs in conversion interfaces because the size of the downloadable result and the size of the working pixels answer different questions. Use the downloadable byte count for transfer requirements and the pixel dimensions when estimating a drawing buffer.

To review a preview implementation, log the source dimensions and chosen destination dimensions for one ordinary image and one image beyond your policy. Check that the second case shows a useful message before allocating the destination canvas. Then try several accepted images in succession and confirm that finished previews do not leave unnecessary work active.

Reference: MDN's canvas optimization guide.

Top comments (0)