Search code examples
htmlcssimage-processingcanvasscaling

HTML canvas upscaling/downscaling algorithm with CSS (image processing)


I have a canvas with a 400x300px coordinates system (set with width and height attributes of the <canvas> DOM element)

Sometimes it has to be resized to a bigger size with the CSS scaling system.

How to change the image upscaling/downscaling algorithm? (bicubic, spline, nearest neighbour, etc.)

I have tested image-rendering: optimizeQuality, image-rendering: crisp-edges, image-rendering: -webkit-optimize-contrast, image-rendering: optimize-contrast, interpolation-mode: nearest-neighbor without success.

Is there nowadays a direct CSS way, without JS low-level image modification (like in HTML5 Canvas Resize (Downscale) Image High Quality?)?

Example with default algorithm (is there a way to change this default algorithm?):

ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 100, 100);
ctx.fillStyle = "#00FF00";
ctx.fillRect(100, 100, 100, 100);
ctx.fillStyle = "#0000FF";
ctx.fillRect(200, 200, 100, 100);
ctx.fillStyle = "#000000";
ctx.fillRect(300, 0, 100, 100);
ctx.font = "50px sans-serif";
ctx.fillText("Hello world", 50, 50);
.canvas-wrapper {
  width: 852px;
}

canvas {
  width: 100%;
  background-color: #ccc;
}
<div class="canvas-wrapper">
  <canvas width="400" height="300"></canvas>
</div>


Solution

  • image-rendering should be the way. It's quite unfortunate that most browsers still don't support more than pixelated & auto values, but that's what's supposed to be used here (with a mention to Firefox that does expose smooth, but which apparently is just the same as auto.

    const canvas = document.querySelector("canvas");
    const ctx = canvas.getContext("2d");
    ctx.fillStyle = "#FF0000";
    ctx.fillRect(0, 0, 100, 100);
    ctx.fillStyle = "#00FF00";
    ctx.fillRect(100, 100, 100, 100);
    ctx.fillStyle = "#0000FF";
    ctx.fillRect(200, 200, 100, 100);
    ctx.fillStyle = "#000000";
    ctx.fillRect(300, 0, 100, 100);
    ctx.font = "50px sans-serif";
    ctx.fillText("Hello world", 50, 50);
    
    const select = document.querySelector("select");
    select.onchange = () => {
        document.body.style.setProperty("--ir-value", select.value);
    };
    // display only the "supported" values
    ["smooth", "high-quality", "crisp-edges", "pixelated", "-webkit-optimize-contrast"].forEach((val) =>
    {
        canvas.style.imageRendering = val;
      if (getComputedStyle(canvas).imageRendering !== "auto") {
        select.append(new Option(val));
      }
      canvas.style.imageRendering = "";
    });
    .canvas-wrapper {
      width: 852px;
    }
    canvas {
      width: 100%;
      background-color: #ccc;
      image-rendering: var(--ir-value);
    }
    <select>
      <option>auto</option>
    </select>
    <div class="canvas-wrapper">
      <canvas width="400" height="300"></canvas>
    </div>

    Note that since the linked question has been posted the canvas API also has been updated and now includes an imageSmoothingQuality setting, which is still not supported in Firefox:

    const canvas1 = document.createElement("canvas");
    canvas1.width = 400;
    canvas1.height = 300;
    {
      const ctx = canvas1.getContext("2d");
      ctx.fillStyle = "#FF0000";
      ctx.fillRect(0, 0, 100, 100);
      ctx.fillStyle = "#00FF00";
      ctx.fillRect(100, 100, 100, 100);
      ctx.fillStyle = "#0000FF";
      ctx.fillRect(200, 200, 100, 100);
      ctx.fillStyle = "#000000";
      ctx.fillRect(300, 0, 100, 100);
      ctx.font = "50px sans-serif";
      ctx.fillText("Hello world", 50, 50);
    }
    
    const select = document.querySelector("select");
    const draw = () => {
      const canvas = document.querySelector("canvas");
      const width = canvas.offsetWidth;
      const height = canvas.offsetHeight;
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
      ctx.imageSmoothingQuality = select.value;
      ctx.drawImage(canvas1, 0, 0, canvas.width, canvas.height);
    }
    select.onchange = draw;
    draw();
    
    if(!("imageSmoothingQuality" in CanvasRenderingContext2D.prototype)) {
      console.error("your browser doesn't support imageSmoothingQuality");
    }
    .canvas-wrapper {
      width: 852px;
    }
    canvas {
      width: 100%;
      background-color: #ccc;
    }
    <select>
      <option>low</option>
      <option>medium</option>
      <option>high</option>
    </select>
    <div class="canvas-wrapper">
      <canvas width="400" height="300"></canvas>
    </div>