javascriptcanvastextfontsantialiasing

How to draw a pixel font on the canvas without anti-aliasing


I have a pixel art font (in a ttf file), I've found it's native resolution to be 8 pixels (CTX.font = '8px mainfont';)

When I do fillText, the font is coming out perfect in firefox, but blurry in chrome:

enter image description here firefox

chrome chrome

I tried offsetting the X coordinate by different amounts like 0.5, but it just gets blurrier. Normally it's always a round integer.

I tried CTX.translate(0.5, 0); and CTX.imageSmoothingEnabled = true;

I tried css font-smooth: none; font-smooth: never; -webkit-font-smoothing : none;

In the past I had to convert my fonts to a special format and use a library for drawing them on canvas. Was just hoping 5 years later they added an official way to fix this problem?


Solution

  • Was just hoping 5 years later they added an official way to fix this problem?

    Well... not more than a few months ago, more text modifiers have been added to the canvas API, among which ctx.textRendering, which is basically the equivalent of SVG's text-rendering.

    So, none of the options will really force turning off anti-aliasing, but you will certainly have better results using textRendering = "geometricPrecision".

    Also, this is currently only being supported by Chromium based browsers ... and only with the chrome://flags/#enable-experimental-web-platform-features turned on.

    const label = document.querySelector( "label" );
    const canvas = document.querySelector( "canvas" );
    const ctx = canvas.getContext( "2d" );
    if( !ctx.textRendering ) {
      console.warn( `Your browser doesn't support the textRendering property on Canvas
    If you are on Chrome be sure to enable chrome://flags/#enable-experimental-web-platform-features` );
    }
    
    let state = 0;
    const states = [
      () => {
        label.textContent = "optimizeLegibility";
        ctx.textRendering = "optimizeLegibility";
        drawText();
      },
      () => {
        label.textContent = "geometricPrecision";
        ctx.textRendering = "geometricPrecision";
        drawText();
      },
      () => {
        label.textContent = "difference";
        ctx.textRendering = "optimizeLegibility";
        drawText();
        ctx.globalCompositeOperation = "xor";
        ctx.textRendering = "geometricPrecision";
        drawText();
        ctx.globalCompositeOperation = "source-over";
      }
    ];
    
    document.fonts.load( "120px pixel" ).then( begin );
    
    function begin() {
      
      ctx.clearRect( 0, 0, canvas.width, canvas.height );
      ctx.font = "120px pixel";
      states[ state ]();
      state = (state + 1) % states.length;
      setTimeout( begin, 1000 );
      
    } 
    function drawText() {
      ctx.textBaseline = "top";
      ctx.fillText( "TESTING", 0, 0 );
    }
    @font-face {
      font-family: pixel;
      src: url("https://dl.dropboxusercontent.com/s/hsdwvz761xqphhb/pixel.ttf");
    }
    <label></label><br>
    <canvas width="500"></canvas>

    For the time being, the best might be to pre-render your texts to bitmap.