stackers
stackers

Reputation: 3270

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?

Upvotes: 8

Views: 2809

Answers (2)

UTunnels
UTunnels

Reputation: 11

textRendering = "geometricPrecision" doesn't do much to improve the result. You'll have better result using a 3rd party library if it is not covenient to pre-render the text to bitmap. For example, opentype.js.

Upvotes: 0

Kaiido
Kaiido

Reputation: 136549

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.

Upvotes: 5

Related Questions