Canvas drawImage - visible edges of a tile in firefox / opera / ie (not chrome)

I draw a map of the game on canvas. Earth made of slabs - simple 64x64 png image.

When I draw it in Chrome, it looks fine (on the left), but when I draw it in Firefox / Opera / IE (on the right), I get visible edges: enter image description here

The problem disappears when I use rounded numbers:

ctx.drawImage(img, parseInt(x), parseInt(y), w, h); 

But this does not help when I use scaling:

 ctx.scale(scale); // anything from 0.1 to 2.0 

I also tried them, but no change:

  • ctx.drawImage(img, 5, 5, 50, 50, x, y, w, h); // so not an issue of clamping
  • ctx.imageSmoothingEnabled = false;
  • image-rendering: -moz-crisp-edges; (css)

Is there any way to make it work in ff / op / ie?


Edit: incomplete solution found

Adding 1 pixel to the width / height and with compensation on a scale (width + 1 / scale) helps:

 ctx.drawImage(props.img, 0, 0, width + 1/scale, height + 1/scale); 

It creates some artifacts, but I find it acceptable. In this image, you can see green tiles with no edges and blue windows that are not compensated, still with visible edges:

fixed

+4
source share
4 answers

The simplest solution (and I would recommend it most efficiently) is to use tiles with an overlap of 1 pixel (1x1 or 2x2 size) when drawing the background tiles of your game.

Nothing unusual, just draw a little more than usual. This avoids complications and performance considerations when introducing additional transformations to the mix.

For instance:

 var img = new Image(); img.onload = function () { for (var x = 0.3; x < 200; x += 15) { for (var y = 0.3; y < 200; y += 15) { ctx.drawImage(img, 0, 0, 15, 15, x, y, 15, 15); // If we merely use 16x16 tiles instead, // this will never happen: //ctx.drawImage(img, 0, 0, 16, 16, x, y, 16, 16); } } } img.src = "http://upload.wikimedia.org/wikipedia/en/thumb/0/06/Neptune.jpg/100px-Neptune.jpg"; 

Before: http://jsfiddle.net/d9MSV

And after: http://jsfiddle.net/d9MSV/1/


Note , as the aiser pointed out, an additional pixel should take into account scaling, so a more correct solution is to modify it: http://jsfiddle.net/d9MSV/3/

+2
source

Cause

This is due to smoothing.

The canvas is still in progress, and the browser has various implementations for anti-aliasing processing.

Possible solutions

1

You can try disabling anti-aliasing for images in Firefox as follows:

 context.mozImageSmoothingEnabled = false; 

In Chrome:

 context.webkitImageSmoothingEnabled = false; 

and add a class to an element like this (should work with Opera):

 canvas { image-rendering: optimizeSpeed; // Older versions of FF image-rendering: -moz-crisp-edges; // FF 6.0+ image-rendering: -webkit-optimize-contrast; // Webkit image-rendering: -o-crisp-edges; // OS X & Windows Opera (12.02+) image-rendering: optimize-contrast; // Possible future browsers. -ms-interpolation-mode: nearest-neighbor; // IE } 

In this browser test, which I did to see the effect of disabling anti-aliasing:

ANTI-ALIAS BROWSER TEST

2

Translate the entire canvas by 0.5 points.

 ctx.translate(0.5, 0.5); 

This does not always work and may contradict other translations. However, you can add a fixed offset each time:

 ctx.translate(scrollX + 0.5, scrollY + 0.5); 

3

Another option is a compromise, which you either impose on the tiles with one additional pixel, which I do not recommend because of the extra work that you will support.

4

This method draws fragments that are slightly scaled, so they overlap:

 ctx.drawImage(tile, x, y, 65, 65); //source tile = 64x64 

This may be enough to cover the failure. In combination with disabling anti-aliasing (or using the nearest neighbor), it will not affect most of the tile graphics, but this can reduce performance due to scaling.

If you turn off anti-aliasing (and this did not work on its own), the overhead will be minimal, as some of them will interpolate the image.

5

Just draw all positions offset -1 (i.e. grid = 63x63). Of course, this will ruin everything with regard to checks, so ...

+2
source

In each fragment of the picture, use Math.floor when there is division, for example:

  ctx.drawImage(image,Math.floor(xpos/3),ypos+1) 

Also, if you have a drawing loop that calls itself, always use requestAnimationFrame. I don’t know why, but since I switched from the timer timeout to requestAnimationFrame, I no longer have artifacts.

0
source

I draw all my tiles in a perfect size buffer and then draw this buffer onto the display canvas using drawImage, which takes care of scaling. If you have 16x16 tiles, make your buffer multiple of 16, like 256x128 or 64x96, or something like that. This eliminates the gaps between the plates that arise due to drawing with scaled dimensions. The only drawback is that you have to do a full background twice: once to draw a background in perfect pixel space, and once to draw a scaled image on the final canvas. Remember to keep the aspect ratio between the buffer and the canvas of the display to avoid distortion of your final image.

0
source

Source: https://habr.com/ru/post/1492101/


All Articles