5

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;
Community
  • 1
  • 1
shabunc
  • 23,119
  • 19
  • 77
  • 102
  • 1
    searla's answer is correct, you are probably on an high dpi monitor. Unrelated, but still important, you need to wait for your image has loaded before being able to draw it on the canvas (you codepen didn't work for me), and only chrome does support drawing svg without and absolute height and width atribute. [Fixed pen](http://codepen.io/anon/pen/MmJXXO) – Kaiido Apr 27 '17 at 12:10

1 Answers1

7

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

searlea
  • 8,173
  • 4
  • 34
  • 37
  • correct but `backingStorePixelRatio` has been deprecated and even removed from implementations (except from Safari's one, under a prefix). – Kaiido Apr 27 '17 at 12:12
  • had I have an opportunity to upvote multiple time I'd just did it, thanks! – shabunc Apr 27 '17 at 12:48