Skip to content Skip to sidebar Skip to footer

Canvas Has Inconsistent Pixel Grid Across Browsers When Using Drawimage()

I recognize that Canvas drawImage is inexplicably offset by 1 Pixel is a very similar issue, but I was already applying the advice given in that question's answer before I even cam

Solution 1:

Sprites can not share edges!

What you are seeing is normal. It is not only the browsers that will give different results, but different hardware and device settings will also show this problem to different degrees.

The problem is in the hardware and these artifacts may appear even when you set all your render values to integers.

Why is this happening?

The reason is because you are trying drawing on two side of the same line. The way GPU's samples images means that there will always be a little bit of error. You can never get rid of it. If you draw at location y = 16 a little bit of pixel 15 will bleed in. If you are using nearest neighbor, a little bit no matter how small equate to a whole display pixel.

The image shows your original sprite sheet. The top sprite's bottom is where the next sprite sprite starts. They share the boundary represented by the red line. A shared boundary will always bleed across.

enter image description here

This is not a browser bug, it is inherent in the hardware. You can not fix it with half offsets. There is only one way to solve the problem.

Simple solution

The solution is very simple. No two sprites may share an edge, you need a 1 pixel transparent space between consecutive sprites.

Image shows sprites on left share edge and bleed across the line. Sprites on right fixed with 1 pixel transparent space.

enter image description here

Eg

If you have sprite 16, by 16 then the coords you are using

// Format x,y,w,h0,0,16,16// right edge16,0,16,16// touches left edge. BAD!
... 
// next row0,16,16,16// Top touch sprite above bottom16,16,16,16// Left edge touches right edge. BAD!

Add a single pixel between all, they can still touch the image edge

0,0,16,16// spr 017,0,16,16// spr 1  No shared edge between them
... 
// next row0,17,16,16// spr 8  no shared edge above17,17,16,16// spr 9

In the render function the source coordinates should always be floored. However the transform, and the destination coordinates can be floats.

ctx.drawImage(image,int,int,int,int,float,float,float,float);

The next image is your sprite sheet correctly spaced. Sprites are still 16 by 16 but they are spaced 17 pixels apart so no two sprites share a boundary.

enter image description here

To draw a sprite by index from top left to right and same for each row down.

var posx,posy; // location of sprite on display canvasvar sprCols = 6; // number of columnsvar sprWidth = 16; 
var sprHeight = 16; 
var sprIndex = ?  // index of sprite 0 top leftvar x = (sprIndex % sprCols) * (sprWidth + 1);
var y = (sprIndex / sprCols | 0) * (sprHeight + 1);
ctx.drawImage(spriteSheet, x, y, sprWidth, sprHeight, posx, posy, sprWidth, sprHeight);

Back to game creation.

Now you can forget that crazy half pixel stuff, its just nuts, as it does not work. This is how how its done, and its been done this way since the first GPU's hit the market in the mid 1990's.

Note All images sourced from OP's fiddle

Solution 2:

I can't tell for IE, but at least for Safari, this sounds like a bug in their nearest-neighbor algorithm when the transformation matrix translate is set to exactly n.5.

onload = function() {

  var ctx = document.querySelector('canvas').getContext('2d');
  ctx.imageSmoothingEnabled = false;
  var img = document.querySelector('#hero');

  ctx.setTransform(2, 0, 0, 2, 99.49, 99.49);
  ctx.drawImage(img, 32, 32, 16, 16, 0, 0, 16, 16);

  ctx.setTransform(2, 0, 0, 2, 99.5, 99.5);
  ctx.drawImage(img, 32, 32, 16, 16, 16, 0, 16, 16);

  ctx.setTransform(2, 0, 0, 2, 99.51, 99.51);
  ctx.drawImage(img, 32, 32, 16, 16, 32, 0, 16, 16);

};
<imgsrc='http://xmpps.greenmaw.com/~coda/html52d/hero.png'id='hero'style='display:none' /><canvaswidth='200'height='200'></canvas>

Result in Safari 11.0.3

Result of previous snippet: the center image contains a bit of the upper sprite

You may want to let them know about it from their bug-tracker.

The workaround would be to make sure that you never lay on floating coordinates, even in your transformation matrix.

Post a Comment for "Canvas Has Inconsistent Pixel Grid Across Browsers When Using Drawimage()"