Reputation: 13
I've got a strange behaviour with a font, the text is blurry following vertical (and horizontal but this is harder to spot) "columns". This is tested on a canvas and on a svg with chrome. The behaviour is almost identical between the two.
Have a look at the jsfiddle : https://jsfiddle.net/ryderone/m9kpjto3/170
Or look at the snippet below.
As you can see, I have very sharp chars, and very blurry chars depending on where it's printed.
// test with canvas
var canvas = document.querySelector('#canvas');
var ctx = canvas.getContext('2d');
var coef = 1; // try to hidpi canvas, increase this to increase the ratio
canvas.width = 300 * coef;
canvas.height = 300 * coef;
document.fonts.onloadingdone = () => {
ctx.fillStyle = '#ffffff';
ctx.imageSmoothingEnabled = false;
ctx.textBaseline = 'middle';
ctx.font = (13 * coef) + 'px visitor2';
for (var i = 1; i < 19; i++) {
var x = 10;
var y = 10 * i * coef + i;
ctx.fillText(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + x + "/" + y, x | 0 + 0.5, y | 0 + 0.5);
}
}
// same test with svg
var svg = document.querySelector('#svg');
for (var j = 1; j < 19; j++) {
var x = 10;
var y = 10 * j + j;
var element = document.createElementNS('http://www.w3.org/2000/svg', 'text');
element.setAttributeNS(null, 'x', x + '');
element.setAttributeNS(null, 'y', y + '');
element.style.fontFamily = 'visitor2';
element.style.fontSize = '13px';
element.style.fill = '#ffffff';
var txt = document.createTextNode(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + x + "/" + y);
element.appendChild(txt);
svg.appendChild(element);
}
#canvas,
#svg {
background: black;
width: 300px;
height: 300px;
}
@font-face {
font-family: visitor2;
src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
<canvas id="canvas"></canvas>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300" height="300" id="svg"></svg>
Note that the run button above is not working properly for the canvas (left side) since I didn't do the font face loading. But it's working on the jsfiddle if you press run multiple times.
I don't think this is a subpixel canvas thing since it's happening also on the SVG, with exactly the same behavior; and I don't think this is a device ratio thing neither. I don't think something is wrong with the font because I've seen the exact same font used in a flash project and it's working perfectly.
I've basically checked all SO questions about blurry text on canvas, and HTML in general. I've found some interesting answers but I didn't found any posts describing this exact behaviour.
Does someone has any idea on what can cause this behaviour and how to solve it ?
Thanks !
EDIT 1 : Here is the picture of the problem
EDIT 2 : Please, do not edit the post to make a thumbnail of this picture, since the resize made by SO hide the actual problem. Click on the link to see the picture in full size. Thank you.
EDIT 3 : I wasn't clear enough in the original post. This is not a canvas related problem since the same problem occurs also on an SVG. Some of the viewers of the post said they were not able to reproduce the problem with their browser, the canvas and the SVG were both crisp clear. I'm thinking about a chrome related problem or something like that.
Upvotes: 1
Views: 3583
Reputation: 136698
This is mostly the fault of your font.
Its kerning is not correctly set and will make this succession of characters fall on floating coordinates.
At some point, the browser will have to trigger font-smoothing algorithm. Different OS and browsers will use different font-smoothing algorithm by default, and screen with different pixel density will have more or less noticeable artifacts; that's probably why others don't see the same result as yours (I do).
Not much you can do apart complaining to the designer, it may even appear with HTML:
@font-face {
font-family: visitor2;
src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#test {
font: 13px visitor2;
line-height: 10px;
padding: 10px;
}
<div id="test">
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
</div>
Here is how it looks on my FF on macOs (sub-pixel antialiasing):
And here is how it looks on my Chrome (grayscale antialiasing):
For the SVG and HTML, you could try to disable the antialiasing, though IIIRC that would work only for Chrome on macOs systems, by using the non-standard font-smooth CSS property to none
. But doing so, the browser will use a nearest-neighbor like algorithm which will just create a gap in your text:
@font-face {
font-family: visitor2;
src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#test {
font: 13px visitor2;
line-height: 10px;
padding: 10px;
font-smooth: none; /* in case it works one day*/
-webkit-font-smoothing: none;
}
<div id="test">
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
</div>
And here is how it looks like on my Chrome:
So yes, there is no blur anymore, but you get a nice gap after 13th character.
For the <canvas>, the default is transparency antialiasing, which is logic since the browser doesn't know what will be drawn behind.
But there is a little-known feature that allows us to force sub-pixel antialiasing on macOs. By initiating our 2D context with the alpha: false
option, most browsers will set the font-smoothing as sub-pixel:
document.fonts.onloadingdone = e => {
// tell our context it won't have transparency
const ctx = canvas.getContext('2d', {alpha: false});
ctx.fillStyle = 'white';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'black';
ctx.font = (13) + 'px visitor2';
for (var i = 1; i < 19; i++) {
var x = 10;
var y = 10 * i + i;
ctx.fillText(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + x + "/" + y, x | 0 + 0.5, y | 0 + 0.5);
}
// draw close-up
ctx.imageSmoothingEnabled = false;
ctx.drawImage(canvas, -70, 70, canvas.width * 5, canvas.height * 5);
};
@font-face {
font-family: visitor2;
src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#canvas {
font: 13px visitor2;
}
<canvas id="canvas" height="200"></canvas>
So now at least, you have the possibility to choose between both antialising methods.
Upvotes: 1