Ruofeng
Ruofeng

Reputation: 2350

drawImage generates bad quality compared to img tag

When I use drawImage to put an image on canvas, it always looks not as sharp as the same image using a tag. I have searched for many solutions, for example, downsampling, smoothingEnabled, but none is helping. How to improve quality of drawImage?

<html>
<head>
</head>
<body>
    <img src="test.png" width="550" height="405">
    <canvas id="image"></canvas>
</body>
<script type="text/javascript">
    var canvas = document.getElementById('image');
    var ctx = canvas.getContext('2d');
    ctx.mozImageSmoothingEnabled = false;
    ctx.webkitImageSmoothingEnabled = false;
    ctx.msImageSmoothingEnabled = false;
    ctx.imageSmoothingEnabled = false;
    canvas.width = 550;
    canvas.height = 405;
    canvas.style.width  = canvas.width.toString() + "px";
    canvas.style.height = canvas.height.toString() + "px";

    var img = new Image();
    img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    };
    img.src = 'test.png';
</script>

You can clearly the difference in their quality

Upvotes: 1

Views: 1163

Answers (1)

Kaiido
Kaiido

Reputation: 136678

You seem to be on a retina-like display. This can be confirmed by looking at the size of your screenshot (2264 × 886).

These displays do have an higher pixel density. To keep the sizes of web-pages consistent with what the author intended, they do up-scale automatically all the content of the page.

This means that your <img> tag is actually drawn at 1100x810px, so is your canvas.
However, this automation is only done at a presentation level (CSS). Your canvas context still contains only 550x405 pixels. So when passed at presentation level, it has to upscale it, via CSS, which produces this loss of quality.

There is no bullet-proof method to know the PPI (Pixels Per Inch) ratio of a screen, but you can still try to get it thanks to the window.devicePixelRatio property. This may be inaccurate if your user does play a lot with its browser's zoom level, but in 90% of cases, it works fine.

Once you've got this, you can do manually for the canvas what the browser does for the <img> : upscale its content and downscale its presentation.

var canvas = document.getElementById('image');
var ctx = canvas.getContext('2d');
// upscale the canvas content
canvas.width = 550 * devicePixelRatio;
canvas.height = 405 * devicePixelRatio;
// downscale the presentation
canvas.style.width = (canvas.width / devicePixelRatio).toString() + "px";
canvas.style.height = (canvas.height / devicePixelRatio).toString() + "px";

var img = new Image();
img.onload = function() {
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = 'https://i.sstatic.net/s15bD.png';
<img width="550" height="405" src="https://i.sstatic.net/s15bD.png" />
<canvas id="image"></canvas>

Upvotes: 8

Related Questions