shabunc
shabunc

Reputation: 24731

How to deal with quality loss using canvas drawImage?

I've seen this question on Stackoverflow, but I failed to find an answer - all solutions proposed just does not work for me.

Problem: When I'm passing a base64-encoded SVG as src to image it looks as crisp as original SVG image. But if I'll take this image and use it in canvas (via context.drawImage) - it would be of a worser quality: enter image description here

The question is - how can I draw svg-based image in canvas which will look like original image?

What have I tried so far. The approach described here (re-iterative downsampling) is not working for me. The other advise (to add 0.5 to the dimensions) has not saved the situation as well. Playing with imageSmoothingEnabled - still no luck.

Here's the codepen used for generating this particular screenshot:

let toBase64 = (svg) => {
  let serialized = new XMLSerializer().serializeToString(svg);
  let base64prefix = "data:image/svg+xml;base64,"
   let enc = base64prefix + btoa(serialized);
  return enc;
}

let copySvg = (svg, img) => {
  let enc = toBase64(svg);
  img.src = enc;
}

let copyImg = (img, canvas) => {
  context = canvas.getContext("2d");
  context.drawImage(img, 0, 0, 200.5, 200.5);
}

let main = () => {
   let svg = document.getElementById("svg");
   let img = document.getElementById("img");
   let canvas = document.getElementById("canvas");
   copySvg(svg, img);
   copyImg(img, canvas);
}

window.onload = main;

Upvotes: 5

Views: 5653

Answers (1)

searlea
searlea

Reputation: 8378

The SVG and Image are implicitly drawn at high DPI, but you need to explicitly handle this case for canvas.

If your device has window.devicePixelRatio === 2, you'll see a much crisper image if you increase canvas size and update drawImage to match:

<!-- Change this -->
<canvas id="canvas" width="200" height="200"></canvas>
<!-- to -->
<canvas id="canvas" width="400" height="400"></canvas>

And:

// change this:
context.drawImage(img, 0, 0, 200.5, 200.5);
// to:
context.drawImage(img, 0, 0, 400, 400);

Forked codepen

For more details about window.devicePixelRatio (and canvas's backingStorePixelRatio) see the html5rocks article on High DPI Canvas

Upvotes: 8

Related Questions