views:

1693

answers:

8

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.

A: 

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

See here

Fabien Ménager
You're right, I had the parameters wrong. Still, I've modified them so it uses the right parameters and still gives the same error.
mr.doob
A: 

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().

A: 

I'm trying to fight exactly the same error right now and so far I found out that graphData is null. Why it is null and why error it gives said something about strings, that is a mistery to me.

vava
A: 

I'm having the same problem, and found out it only occurs when putImageData tries to write outside the canvas. It works fine in Google Chrome, but firefox doesn't like it.

Can't think of a way round this at the moment, so any answers will be really helpful!

Greg
+4  A: 

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.

bcherry
This was my problem, too. Instead of clipping my data, I just made the canvas bigger and the exception stopped being thrown.
Jason Diamond
A: 

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..

Jackson
On one of my projects, I added a try/catch around the putImageData function. Worst case you get a frame with no image as opposed to a broken program. It's no solution, but yeah..
Jackson
A: 

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

Jackson
+3  A: 

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
    
rogerh
This answer saved me half-a-days work. Thank you.
michael