Tutorial :Firefox throwing a exception with HTML Canvas putImageData



Question:

So I was working on this little javascript experiment and I needed a widget to track the FPS of it. I ported a widget I've been using with Actionscript 3 to Javascript and it seems to be working fine with Chrome/Safari but on Firefox is throwing an exception.

This is the experiment:

Depth of Field

This is the error:

[Exception... "An invalid or illegal string was specified"  code: "12" nsresult: "0x8053000c (NS_ERROR_DOM_SYNTAX_ERR)"  location: "http://mrdoob.com/projects/chromeexperiments/depth_of_field__debug/js/net/hires/debug/Stats.js Line: 105"]  

The line that is complaning about is this one:

graph.putImageData(graphData, 1, 0, 0, 0, 69, 50);  

Which is a crappy code to "scroll" the bitmap pixels. The idea is that I only draw a few pixels on the left of the bitmap and then on the next frame I copy the whole bitmap and paste it on pixel to the right. This error usually is thrown because you're pasting a bitmap bigger than the source and it's going off the limits, but in theory that shouldn't be the case as I'm defining 69 as the width of the rectangle to paste (being the bitmap 70px wide).

And this is full code:

var Stats = {  baseFps: null,    timer: null,  timerStart: null,  timerLast: null,  fps: null,  ms: null,    container: null,  fpsText: null,  msText: null,  memText: null,  memMaxText: null,    graph: null,  graphData: null,     init: function(userfps)  {      baseFps = userfps;        timer = 0;      timerStart = new Date() - 0;      timerLast = 0;       fps = 0;      ms = 0;        container = document.createElement("div");      container.style.fontFamily = 'Arial';      container.style.fontSize = '10px';      container.style.backgroundColor = '#000033';      container.style.width = '70px';      container.style.paddingTop = '2px';        fpsText = document.createElement("div");      fpsText.style.color = '#ffff00';      fpsText.style.marginLeft = '3px';      fpsText.style.marginBottom = '-3px';      fpsText.innerHTML = "FPS:";      container.appendChild(fpsText);        msText = document.createElement("div");      msText.style.color = '#00ff00';      msText.style.marginLeft = '3px';      msText.style.marginBottom = '-3px';      msText.innerHTML = "MS:";      container.appendChild(msText);        memText = document.createElement("div");      memText.style.color = '#00ffff';      memText.style.marginLeft = '3px';      memText.style.marginBottom = '-3px';      memText.innerHTML = "MEM:";      container.appendChild(memText);        memMaxText = document.createElement("div");      memMaxText.style.color = '#ff0070';      memMaxText.style.marginLeft = '3px';      memMaxText.style.marginBottom = '3px';      memMaxText.innerHTML = "MAX:";      container.appendChild(memMaxText);        var canvas = document.createElement("canvas");      canvas.width = 70;      canvas.height = 50;      container.appendChild(canvas);        graph = canvas.getContext("2d");      graph.fillStyle = '#000033';      graph.fillRect(0, 0, canvas.width, canvas.height );        graphData = graph.getImageData(0, 0, canvas.width, canvas.height);        setInterval(this.update, 1000/baseFps);        return container;  },    update: function()  {      timer = new Date() - timerStart;        if ((timer - 1000) > timerLast)      {          fpsText.innerHTML = "FPS: " + fps + " / " + baseFps;          timerLast = timer;            graph.putImageData(graphData, 1, 0, 0, 0, 69, 50);          graph.fillRect(0,0,1,50);          graphData = graph.getImageData(0, 0, 70, 50);            var index = ( Math.floor(Math.min(50, (fps / baseFps) * 50)) * 280 /* 70 * 4 */ );          graphData.data[index] = graphData.data[index + 1] = 256;            index = ( Math.floor(Math.min(50, 50 - (timer - ms) * .5)) * 280 /* 70 * 4 */ );          graphData.data[index + 1] = 256;                        graph.putImageData (graphData, 0, 0);            fps = 0;                  }        ++fps;        msText.innerHTML = "MS: " + (timer - ms);      ms = timer;  }  

}

Any ideas? Thanks in advance.


Solution:1

It's a bug in Firefox. Mozilla knows about it. Here's the workaround:

  1. Make a new in-memory canvas:

    var spriteCanvas = document.createElement('canvas');  
  2. Set the height/width of the canvas to the height/width of your image:

    spriteCanvas.setAttribute('width', 20);  spriteCanvas.setAttribute('height', 20);  
  3. Put the image data into the canvas at position (0,0):

    spriteCanvas.getContext('2d').putImageData(imageData, 0, 0);  
  4. On the context for your main canvas, draw your canvas sprite using drawImage using any position on-screen or off-screen:

    context.drawImage(spriteCanvas, 50, 100); // clipping is allowed  


Solution:2

Yeah, this error seems to happen when using (get|put)ImageData off the edges of the canvas.

For my encounter with this problem, I simply added some clipping logic that checks the edges of the region to be drawn against the edges of the canvas, and clips it where necessary, only drawing the visible region. It seems to have fixed the problem.


Solution:3

I had the same error because the value I tried to set to graphData.data[index] was a string and not an integer. To fix it, i converted the string in an integer with parseInt().


Solution:4

I was having similar problem with drawImage(), when attempted to draw a tile on a canvas of the same size in this manner:

var image = new Image();       image.onload = function() {           context.drawImage(image, 0, 0, this.tileSize, this.tileSize);         }       image.src = this.baseURL + tileId;  

The problem disappeared, when I removed height and width parameters from the call and used this:

context.drawImage(image, 0, 0);  


Solution:5

I think you need to specify two more arguments to putImageData : width and height.

See here


Solution:6

I've been dealing with this issue for a few days now (in firefox). The best idea I could come up with is to dynamically remove the pixels that are going off the screen. I'm worried that it will take too much cpu to do this. hopefully someone comes up with a better idea :/

but to reiterate, I've only seen this error when putImageData is trying to draw ANY pixel outside of the canvas perimeter. eg. any x or y value < 0.

-- on second read -- It looks like you're already resizing your image data. You might try sizing your image down to 68px if you're in a 70px container? Probably won't work, but worth a try..


Solution:7

This is old by now, but this ONLY giving me problems on FF on windows. FF on mac works how it should.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »