0

I desperately seek for somewhere to discuss this strange bug i found in firefox. But it seem hard to meet some mozilla crew.

Context is very simple: WebGL 2.0, drawing text using the well known fontmap technic with point-sprite.

One image is better than 1000 words:

Firefox vs Chromium

On the right Chromium, all is ok, on the left Firefox... and uhhggh ?!

Questions:

  • Why the text is yellow in Firefox despite the fact it should be white ? ?

  • Why the text have strange black pixels in firefox ?

  • This seems to be a kind of "sharpen" filter... but WHY ?

Some details :

  • This is exactly the same code for both browsers.

  • The fontmap texture is generated using an "off-screen" canvas, this is RGBA with RGB all white and characteres printed in alpha channel. I verified the generated picture on both browsers, they are not exactly the same, but all appear OK (no strange pixel nor black border, etc...), the problem seem to be not here

  • The WebGL texture is RGBA/RGBA/UNSIGNED BYTE (as usual), MIN and MAG filter to NEAREST, no mipmaps, with WARP S/T Clamp to edge (but this change nothing, so it doesn't matter)

  • The generated texture is NPOT, but i don't think the problem is here.

  • The Blend equation used to render the text is (the usual) SRC_ALPHA, ONE_MINUS_SRC_ALPHA

  • I tested with the blend equestion SRC_ALPHA, ONE and in this case Firefox acts correctly (but additive blending is not what i want !).

  • Firefox Version: 55.0.2 (64 bits) Mozilla Firefox for Ubuntu

  • Chromium version: 60.0.3112.113 Built on Ubuntu, running on Ubuntu 16.04 (64 bits)

Here is the fragment shader (using Point-Sprite to draw each char):

precision highp float;
in float chr_c;
uniform vec4 material_col;
uniform sampler2D spl2d_col;
vec2 chr_u; 
out vec4 fragColor;
void main(void) {
  chr_u.x = (gl_PointCoord.x + mod(chr_c, 48.0)) * 0.020833333;
  chr_u.y = (gl_PointCoord.y + floor(chr_c / 48.0)) * 0.020833333;
  fragColor = texture(spl2d_col, chr_u) * material_col;
}

Here is the code used to generate the fontmap texture:

var i, s, x, y, m, w, h, a, o, mx, cv, ct;

mx = c*c; // 'c' is rows/columns count, (here: 48 rows * 48 cols)

cv = document.createElement('canvas');
ct = cv.getContext('2d');

x = 0;
y = 0;
m = 65535;

// determins the size for cells according chars size
for(i = 0; i < mx; i++) {
  s = String.fromCharCode(i);
  w = ct.measureText(s).width;
  h = ct.measureText(s).height;
  if(x < w) x = w;
  if(y < h) y = h;
  if(y < m && (y > 0)) m = y;
}

var r = Math.ceil((y+(y-m)>x)?y+(y-m):x);

w = r * c;
h = r * c;

cv.width = w;
cv.height = h;

ct.fillStyle = 'rgba(255,255,255,0.0)';
ct.fillRect(0, 0, w, h);

ct.font = 'normal ' + p + 'pt ' + f;
ct.fillStyle = 'rgba(255,255,255,1.0)';
ct.textAlign = 'left';
ct.textBaseline = 'top';

for(i = 0; i < mx; i++) {

  a = Math.floor(i % c); // cell Easting (Abscisse a = X)
  o = Math.floor(i / c); // cell Northing (Ordonnée o = y)

  ct.fillText(String.fromCharCode(i), (a*r)+3, (o*r)+2);
}

var gl = this._gl;
this._blank_fnt = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this._blank_fnt);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ct.getImageData(0,0,w,h));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D, null);

cv.remove();

here is the (simplified) code used to draw the text:

gl.enable(gl.BLEND);
gl.depthMask(gl.TRUE);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);

gl.useProgram(le_shader);
gl.activeTexture(gl.TEXTURE0);

gl.uniformMatrix4fv(le_uniform1, le_view.matrix);
gl.uniformMatrix4fv(le_uniform2, le_transform.matrix);
gl.uniform4fv(le_uniform3, le_text.color);

gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.bindTexture(gl.TEXTURE_2D, this._blank_fnt);

gl.bindVertexArray(le_text.vao);
gl.drawArrays(gl.POINTS, 0, le_text.count);
gman
  • 100,619
  • 31
  • 269
  • 393
  • 1
    Does `canvas.getContext("webgl", { premultipliedAlpha: false})` solve your issue? – gman Sep 08 '17 at 02:02
  • Also try `canvas.getContext("webgl", {premultipliedAlpha: false, antialias: false})` and `canvas.getContext("webgl", {antialias: false})`. Note that the issue **may** be that you're using unpremultiplied alpha based on your blend function. By default the canvas expects premultiplied alpha. The values you're putting in the canvas are invalid (they aren't premultiplied) so how they get displayed is undefined (different in different browsers). See [this question and linked answers](https://stackoverflow.com/questions/46093008/webgl-unexpected-effects-in-canvas-when-setting-alpha-lower-than-1-0) – gman Sep 08 '17 at 07:15
  • Yep ! that was the premultiplied alpha... this thing seem to upset many people in several situation. Why the hell firefox insist to set this value to true by default ? –  Sep 09 '17 at 01:29
  • Because it's [defined so in the spec](https://www.khronos.org/registry/webgl/specs/latest/1.0/#WEBGLCONTEXTATTRIBUTES) and it's assumed that developers set their context up in a non ambiguous way. – LJᛃ Sep 10 '17 at 10:48
  • This mention appears more as an example, and except here, it is writen nowhere what context attribute should be true or false by default. The common sense would be to set this attribute to false by default, since such behavior simply does not exists in OpenGL. This ambiguity is an HTML Canvas inheritance. –  Sep 10 '17 at 20:40

0 Answers0