I'm using Raphael to draw an object, then transferring it to an HTML canvas element with canvg so that I can use toDataURL to save it as a PNG. But when I use canvg, the resulting image is blurry. The code below, for example, produces this (raphael on top, canvg on bottom):
<html>
<head>
<script src="lib/raphael-min.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/rgbcolor.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/StackBlur.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/canvg.js"></script>
<script src="lib/raphael.export.js"></script>
</head>
<body>
<div id="raph_canvas"></div><br>
<canvas id="html_canvas" width="50px" height="50px"></canvas>
<script language="JavaScript">
var test=Raphael("raph_canvas",50,50);
var rect=test.rect(0,0,50,50);
rect.attr({fill: '#fff000', 'fill-opacity':1, 'stroke-width':1})
window.onload = function() {
var canvas_svg = test.toSVG();
canvg('html_canvas',canvas_svg);
var canvas_html = document.getElementById("html_canvas");
}
</script>
</body>
</html>
The blurriness is evident in the png created by toDataURL as well. Any idea what is going on here? I don't think this has anything to do with re-sizing. I've tried setting ignoreDimensions: True and some other things.
Another datapoint. If I use raphael to output some text and then use canvg, it is not only blurry but the wrong font!
And here is the test.rect(0.5,0.5,50,50) suggested. Still blurry:
Another solution described in this article, similar to the one posted here, except it's using scale()
and it's taking into account the pixel ratio of the backing store (browser underlying storage of the canvas):
var devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1,
ratio = devicePixelRatio / backingStoreRatio;
// upscale the canvas if the two ratios don't match
if(devicePixelRatio !== backingStoreRatio){
// adjust the original width and height of the canvas
canvas.width = originalWidth * ratio;
canvas.height = originalHeight * ratio;
// scale the context to reflect the changes above
context.scale(ratio, ratio);
}
// ...do the drawing here...
// use CSS to bring the entire thing back to the original size
canvas.style.width = originalWidth + 'px';
canvas.style.height = originalHeight + 'px';
So it took me a while, but then it dawned on me. All your example images are twice the size the code claims they should be. So you're most likely on some sort of HDPI device (Retina MacBook Pro ect...) SVG is great because its resolution independent, canvas on the other hand is not. The issue you're seeing has to do with how canvas renders. To fix this, you need to prep the canvas so that your drawing will be done at the resolution of your screen.
http://jsbin.com/liquxiyi/3/edit?html,js,output
This jsbin example should look great on any screen.
The trick:
var cv = document.getElementById('box');
var ctx = cv.getContext("2d");
// SVG is resolution independent. Canvas is not. We need to make our canvas
// High Resolution.
// lets get the resolution of our device.
var pixelRatio = window.devicePixelRatio || 1;
// lets scale the canvas and change its CSS width/height to make it high res.
cv.style.width = cv.width +'px';
cv.style.height = cv.height +'px';
cv.width *= pixelRatio;
cv.height *= pixelRatio;
// Now that its high res we need to compensate so our images can be drawn as
//normal, by scaling everything up by the pixelRatio.
ctx.setTransform(pixelRatio,0,0,pixelRatio,0,0);
// lets draw a box
// or in your case some parsed SVG
ctx.strokeRect(20.5,20.5,80,80);
// lets convert that into a dataURL
var ur = cv.toDataURL();
// result should look exactly like the canvas when using PNG (default)
var result = document.getElementById('result');
result.src=ur;
// we need our image to match the resolution of the canvas
result.style.width = cv.style.width;
result.style.height = cv.style.height;
This should explain the issue you're having, and hopefully point you in a good direction to fix it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With