Subpixel Anti-Aliased Text on HTML5's Canvas Element

Subpixel anti-aliased text on HTML5's canvas element

It's now possible to get sub-pixel font rendering by creating an opaque canvas context. In Safari and Chrome you can get this using this snippet:

var ctx = canvas.getContext("2d", {alpha: false})

I found this from this blog post.

HTML5 Canvas avoid any subpixel rendering

Well after some exhaustive research there seems to be no standardized way to stop what I'm experiencing.

Which is anti-aliasing that depending on the browser can only be enabled or disabled for certain drawing operations.

There are a few tricks on how to avoid anti-aliasing in some situations in this stack overflow question: Can I turn off antialiasing on an HTML <canvas> element?

But the only one that will work for me is to manually implement my own drawing functions to produce the shapes I want without any anti-aliasing. This will be done with the canvas putImageData function and there is a good tutorial right here on ways of using this.

For the time being there is no api supported solution for the problem

Is there a technical reason why canvas's drawImage() doesn't do sub-pixel rendering / antialiasing?

For older browsers, you could animate a sprite. Create maybe 4 versions of your image, each shifted 0.25px to the left from the previous one. Paste those together in a sprite and then animate the background-position.

function moveClouds(n)
{
var v = (n % 4) * 417;
var h = Math.ceil(n / 4);
clouds.style.backgroundPosition = h + " " + v;
}

HTML5 Canvas stroke not anti-aliased

My co-worker just pointed out that I need to use clearRect to clear the canvas after each draw. The strokes were just being drawn on top of each other.

function calc(myVal) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var radius = 70;
ctx.clearRect(0, 0, canvas.width, canvas.height);

ctx.beginPath();
ctx.arc(140, 140, 20, myVal * Math.PI, 0, true);
ctx.lineWidth = 14;
ctx.stroke();
};

Sub-pixel rendering in Chrome Canvas

Short answer: No. Not possible

This is one of two topics that frustrates a lot of Canvas users.

Subpixel rendering/anti-aliasing of any kind is up to the browser. This means that different browsers are prone to render things in different ways.

A lot of people have asked for anti-aliasing to be an option that can be turned on or off for a specific context. No luck of anything like that yet.

Chrome in particular you'll need to keep an eye on, because the way they have handled sub-pixel rendering has changed drastically over the past 4 months. If you start using the Chrome developer channel you'll get a preview of the things they keep trying out. They've been doing quite a bit of testing in this area, and have even pushed some drastic regressive changes that I've complained about.

The takeaway here is that:

  1. Chrome is most definitely "not done yet" with regards to subpixel rendering. It sucks to say, but your best option for now is to wait a while.
  2. The spec needs to be a lot more specific in this area so there is some consistency across browsers, because any subpixel rendering/anti-aliasing at all right now is very browser-dependent. There was unresolved discussion of it back in 2008. I'm not away of any progress since.

Can I turn off antialiasing on an HTML <canvas> element?

For images there's now context.imageSmoothingEnabled= false.

However, there's nothing that explicitly controls line drawing. You may need to draw your own lines (the hard way) using getImageData and putImageData.

No subpixel positioning on small HTML5 canvas on chrome

I can definitely reproduce it on both my stable chrome and on my canary.

I reported to the chromium team. Let's hope a fix will come soon enough.

For a workaround, you can shrink a little bit your images (minimum value I found was size * 0.99. This should force the antialiasing algorithm to kick in.

var outer = [200, 200];var inner = [200, 200];
function CreateCanvas(w, h, hidden) { var canvas = document.createElement('canvas'); if (!hidden) document.body.appendChild(canvas); canvas.width = w; canvas.height = h; var context = canvas.getContext('2d'); return { canvas: canvas, context: context };}
function rgba2hex(color) { return "rgba(" + Math.floor(color[0] * 255) + ',' + Math.floor(color[1] * 255) + ',' + Math.floor(color[2] * 255) + ',' + color[3] + ")";}
function GetSystemTimeMS() { return (new Date()).getTime();}
function GetTimeDifferenceMS(time) { return GetSystemTimeMS() - time;}
var outerFontSize = Math.min(100, outer[1] * 0.3);var innerFontSize = Math.min(100, inner[1] * 0.3);
var outerBuffer = CreateCanvas(outer[0], outer[1], false);outerBuffer.context.font = outerFontSize + "px times";outerBuffer.context.fillStyle = rgba2hex([0, 0, 0, 1]);
var innerBuffer = CreateCanvas(inner[0], inner[1], true);innerBuffer.context.font = innerFontSize + "px times";innerBuffer.context.fillStyle = rgba2hex([0, 0, 0, 1]);innerBuffer.context.fillText("raster", 10, inner[1] * 0.9);
var startTime = GetSystemTimeMS();
function draw() { var span = 5; var phase = ((GetTimeDifferenceMS(startTime) / 1000) % span) / span; outerBuffer.context.clearRect(0, 0, outer[0], outer[1]); var x = 50 + phase * 20;
outerBuffer.context.fillText("vector", x, outer[1] * 0.5); // shrink a little bit our image outerBuffer.context.drawImage(innerBuffer.canvas, x, 0, 200 * 0.99, 200 * 0.99);
requestAnimationFrame(draw);}draw();

HTML5 canvas text anti-aliases in chrome, not in firefox

To solve this problem to achieve cross-browser compatibility, and considering it's used apparently for a game, I would suggest a different approach by converting and using it as a bitmap font instead.

You can convert the font in question to a sprite-sheet and then build a simple custom function to render the text.

The process is simple and the performance in more than adequate if there is not large amount of text that needs to be rendered.

The Basics

Here is an example:

  • The font is converted to a bitmap font, basically a mono-spaced sprite-sheet, optimized size-wise (and converted here to a data-uri).
  • Important: when generated only ASCII characters from and including 32 (space) are rendered, up to and including char 128.
  • A custom function is made to parse each character in the string. The char is converted to an ASCII index and 32 is subtracted as we skipped that when making the sprite-sheet.
  • A region in the sprite-sheet is calculated and then rendered directly to canvas at current position (x + string-index * character-width, y as-is).

// Note: font sprite-sheet premade using: 
// https://jsfiddle.net/epistemex/bdm3tbtu/
var ctx, cw = 8, ch = 19, img = new Image; img.onload = go; img.src = bmp;
function go() {
ctx = c.getContext("2d");
// Custom text drawing function demo:
myFillText(ctx, "My custom text fill function", 12, 8);
};

function myFillText(ctx, str, x, y) {
x |= 0; y |= 0; // force x/y to integer positions
for(var i = 0, ascii; i < str.length; i++) {
// get ASCII code but offset -32 to match sprite-sheet
ascii = str.charCodeAt(i) & 0xff - 32;
// look-up bitmap font sprite-sheet and draw directly to canvas
ctx.drawImage(img, ascii * cw, 0, cw, ch, x + i * cw, y, cw, ch);
}
}
<canvas id=c width=600></canvas>
<script>
var bmp = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAvgAAAATBAMAAAAXEGS4AAAAG1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUUeIgAAAACHRSTlMAljxY3CJ2uKuwIxoAAANySURBVFjD7VhNa9tAEH2SZUlH0ULPatJSH00/IEdBc8jREAg6OqSBHg29+LiR43R+djMfm9VXKKREDcRPrFc7mtGbeVptNsIBBxxwwP/GrMQBEyO53kNAt1BsdydoISrwPJgRbTA9Eq2nnpS7LiKWct/P5dte+6ujSsWu6q/t628vKzwH4htgUWJyfL7W3mE6xA0ioFcvAVnpkDtCfVwWYGRF8sku5pzg6RnGkBMBOfeOm/gT28hx77w9gDTOQe0f2FRZDIEsjpuMmZ/UmVx+b9eMuBkHfJ9bb4f52WG5CDimiCA4lpQm4Ue0QaTatgRkOwq95fcSgqz6deFVIkt4CEsCD3TI5ZetHfJA5gWA2ivURHf3Zj2srDASRw1QC8AyELyv9ebrKB8vHiSxzshnZbpq1T8JP1Kn4ifrrvgMfjoxLTkImFF5tgO8ksm7y4r7DvTmNhvIFPVnXfGZO9uL0YfKIC3jHTWhFD0gAhD6xXPTPGlQevAdKZ7V1FgTPwIKGAjT8GNm4ufLgfjiiuRGxVqcol6ZRjyOChsECI28a+jJHRYXckH8sNyYR87J5Oswj+DLBzceP1o8jRWPQfGSnnIpt9XzBrgKVUzDj2yt4qebjoZB/NnPDTumm5PlF4RkbX3XEQUlKecfczQivqwuZH8LqE9lwcAFfoAKEp+H4imMiVrFkwgonCIA1JfgY7rFW24S5+v392qDMA3/vIGKj8VqQev5iPhHDY/rMtkXPfFtYDAiYoJQFCdl9PZwtGcMZj4B57gD0WMzjzqvsl+P5Sy34pXtvheeQfFBeGnhCQRMxo9kqeLHFQpsb/tJuIvZx4YdT8pYL2pGI+IbF9NTeEi+tZM2RyNB517n8+axNVesw+L1l9AqPqfhmmv+ZhPROjkGTMbfX/MHM78+f19xwKKKaZN5hdu+zvdiHux2tO+Ir+beAyRt6XyXUUojuw2gX1DwCDLYmzT0JdcpXj00gaH4k/D/Xfw53YLVTOl3THuYvOPiOygxSQv7Ye7966bnECRNe59v4mPb0B0th/tsgAdWiDRyYa0N+2zAXnzlDdziZfHtfT4PBuJPwY+osq1m8aRvO0z5dKQjdDIJMvwLpLonQARRTMLf/idrcmRjKb7RyfgqMN+p+CcrvAxkFbDFK0GuH9ZezlyriV7RV+wIAX8AnAnL3b7xqFYAAAAASUVORK5CYII=";
</script>


Related Topics



Leave a reply



Submit