Question

I have a class named Engine.Renderer. It just creates a new canvas and give me the possibility to easily update and render the active canvas' scene. When a new canvas is created, I apply those settings to its context:

this.context.imageSmoothingEnabled       = false;
this.context.mozImageSmoothingEnabled    = false;
this.context.webkitImageSmoothingEnabled = false;

In CSS, I've added those lines:

main canvas {
    position: absolute;
    top: 0;

    image-rendering: optimizeSpeed;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: optimize-contrast;
    -ms-interpolation-mode: nearest-neighbor
}

I have also write a function that adjusts the canvas to the window:

[...]
resize: function () {
    var width  = window.innerWidth;
    var height = width / 16 * 9;

    if ( height > window.innerHeight ) {
        height = window.innerHeight;
        width  = height * 16 / 9;
    }

    if ( width > window.innerWidth ) {
        width  = window.innerWidth;
        height = width / 16 * 9;
    }

    width  = Number( width ).toFixed();
    height = Number( height ).toFixed();

    this.canvas.style.width  = width  + "px";
    this.canvas.style.height = height + "px";

    this.container.style.width  = width  + "px";
    this.container.style.height = height + "px";
    this.container.style.left   = ( ( window.innerWidth - width ) / 2 ) + "px";

    // the new scale
    this.scale = ( width / this.canvas.width ).toFixed( 2 );
}
[...]

Now, I have a class named Character. This class is able to create and render a new character on the given canvas. The render part looks like this:

context.drawImage(
    this.outfit,

    this.sprite    * this.dimension.width,
    this.direction * this.dimension.height,

    this.dimension.width,
    this.dimension.height,

    this.position.x,
    this.position.y,

    // set the character sizes to normal size * scale
    this.dimension.width  * this.renderer.scale,
    this.dimension.height * this.renderer.scale
);

I have two problems with it:

  1. the game performance is even worse than before (~9 FPS when rendering a single character on ~1400x800px canvas);
  2. character' image is not so much blurred as before, but I still can see a little blur;

How can I solve those problems?

Was it helpful?

Solution

Try using integer values for positions and sizes:

context.drawImage(
    this.outfit,

    this.sprite    * this.dimension.width,
    this.direction * this.dimension.height,

    this.dimension.width,
    this.dimension.height,

    this.position.x|0,          // make these integer
    this.position.y|0,

    // set the character sizes to normal size * scale
    (this.dimension.width  * this.renderer.scale)|0,
    (this.dimension.height * this.renderer.scale)|0
);

Also, setting canvas size with CSS/style will affect interpolation. From my own tests the CSS settings for interpolation does not seem to affect canvas content any longer.

It's better, if you need a fixed small size scaled up, to set the canvas size properly and instead use scale transform (or scaled values) to draw the content:

this.canvas.width  = width;
this.canvas.height = height;

Update: Based on the comments -

When changing the size of the canvas element the state is reset as well meaning the image smoothing settings need to be reapplied.

When image smoothing is disabled the browser will use nearest-neighbor which means best result is obtained when scaling 2^n (2x, 4x, 8x or 0.5x, 0.25x etc.) or otherwise "clunkyness" may show.

A modified fiddle here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top