Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subpixel anti-aliased text on HTML5's canvas element

I'm a bit confused with the way the canvas element anti-aliases text and am hoping you all can help.

In the following screenshot the top "Quick Brown Fox" is an H1 element and the bottom one is a canvas element with text rendered on it. On the bottom you can see both "F"s placed side by side and zoomed in. Notice how the H1 element blends better with the background:

example 'F'

Here's the code I'm using to render the canvas text:

        var canvas = document.getElementById('canvas');
        if (canvas.getContext){

            var ctx = canvas.getContext('2d');
            ctx.fillStyle = 'black';
            ctx.font = '26px Arial';
            ctx.fillText('Quick Brown Fox', 0, 26);
        }

Is it possible to render the text on the canvas in a way so that it looks identical to the H1 element? And why are they different?

like image 647
Matt Mazur Avatar asked Dec 29 '10 01:12

Matt Mazur


4 Answers

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.

like image 125
sirwart Avatar answered Nov 04 '22 23:11

sirwart


Answering my own question:

It is possible using the technique demonstrated on this site:

https://bel.fi/alankila/lcd/

The only problem is that its too slow to implement in a production app. If anyone runs across a faster way please let me know.

like image 40
Matt Mazur Avatar answered Nov 04 '22 21:11

Matt Mazur


Matt, I sat with the (same/similar) problem last week, which, in my case, turned out to be because of differences in pixel densities on the devices I was testing; I wrote about it tonight - http://joubert.posterous.com/crisp-html-5-canvas-text-on-mobile-phones-and

The link at posterous is dead, so here is the gist with the source code: https://gist.github.com/joubertnel/870190

And the snippet itself:

  // Output to Canvas without consideration of device pixel ratio
  var naiveContext = $('#naive')[0].getContext('2d');    
  naiveContext.font = '16px Palatino';
  naiveContext.fillText('Rothko is classified as an abstract expressionist.', 10, 20);

  // Output to Canvas, taking into account devices such as iPhone 4 with Retina Display
  var hidefCanvas = $('#hidef')[0];
  var hidefContext = hidefCanvas.getContext('2d');

  if (window.devicePixelRatio) {
    var hidefCanvasWidth = $(hidefCanvas).attr('width');
    var hidefCanvasHeight = $(hidefCanvas).attr('height');
    var hidefCanvasCssWidth = hidefCanvasWidth;
    var hidefCanvasCssHeight = hidefCanvasHeight;

    $(hidefCanvas).attr('width', hidefCanvasWidth * window.devicePixelRatio);
    $(hidefCanvas).attr('height', hidefCanvasHeight * window.devicePixelRatio);
    $(hidefCanvas).css('width', hidefCanvasCssWidth);
    $(hidefCanvas).css('height', hidefCanvasCssHeight);
    hidefContext.scale(window.devicePixelRatio, window.devicePixelRatio);               
  }

  hidefContext.font = "16px Palantino";
  hidefContext.fillText("Rothko is classified as an abstract expressionist.", 10, 20);
like image 9
Joubert Nel Avatar answered Nov 04 '22 21:11

Joubert Nel


Here's a way of doing sub-pixel rendering for any canvas content (text, images, vectors, etc). http://johnvalentine.co.uk/archive.php?art=tft.

Outline of the method

It draws onto a canvas, which is then drawn to the screen to take advantage of RGB-striped subpixels. It works with alpha channels too. Note that this might not work if you are using a portrait display, non-striped pixels, or if your browser displays canvases at a lower resolution than your display.

There's scope for fine-tuning, but it's a big gain for a simple method.

like image 2
j5v Avatar answered Nov 04 '22 21:11

j5v