1
votes

I'm just learning how to use the HTML5 canvas element. I've been successful in drawing rectangles, calling getImageData, manipulating pixels and then putImageData to see the rectangles change colour, etc.

Now I'm trying to load an image into the canvas I've hit a snag. After calling drawImage on the canvas's context, fillRect only draws in areas that don't have an image drawn into them, like it's drawing the rectangles behind the images, even though it's called after drawImage. Also, putImageData stops working, even on the visible areas containing rectangles, my pixel manipulation no longer happens. If I comment out the line with drawImage, it works again.

What I want to be able to do is manipulate pixels in the image the same way I was doing with the rectangles. Is there any reason why this wouldn't work?

Draw Image code:

var img = new Image();
img.onload = function () {
     //comment out the following line, everything works, but no image on canvas
     //if it's left in, the image sits over the rectangles, and the pixel
     //manipulation does not occur
     context.drawImage(img, 0, 0, width / 2, height / 2);
}
img.src = path;

Draw Rectangles code:

for (var i = 0; i < amount; i++)
{
    x = random(width - size);
    y = random(height - size);
    context.fillStyle = randomColor();
    context.fillRect(x, y, size, size);
}

Maniuplate pixels code:

var imgd = context.getImageData(0, 0, width, height);
var pix = imgd.data;

//loop through and do stuff

context.putImageData(imgd, 0, 0);
2
Not entirely sure but this could be a side-effect of a cross-domain issue. Is the image you are using hosted on the same domain as the rest of your code?Jani Hartikainen
Thanks for the suggestion, but I don't think this could be the problem as files are sitting in a local folder.Chris Johnson
Nope, I was wrong. Apparently local files are treated differently to web server hosted files which was causing a security exception. Thanks Jani.Chris Johnson

2 Answers

2
votes

Nothing looks particularly wrong with the code you posted. Give us the whole code or consider the following:

  1. Are you changing globalCompositeOperation anywhere?
  2. is randomColor() making colors that are too transparent to see?
  3. Is there some clipping region you haven't mentioned?
  4. Are you drawing everything to the correct contexts?
  5. Is there a spelling mistake that is stopping execution and causing an error?
  6. Its possible your sequence of events is wrong. You could be loading the image, then drawing rectangles, then finish loading the image, so that it appears as if the rectangles were drawn behind your image when that was simply the natural order of things.
  7. Is putImageData causing a security error (easier to see in Firefox) because an image has been drawn to the canvas that was from a different domain? This error might stop all subsequent drawing code execution and therefore give the effect you are describe. See if you are violating one of these rules. Specifically drawing an image to a canvas from a different origin and then trying to use getImageData.

It is most likely the last item or second-to-last item on my list that is giving you grief.

Try hosting the image on your own server and see if it goes away, or look at the web console in Firefox to see if it is complaining about a security error.

Or just open the web console in Chrome/IE9 and see if the drawing code actually gets hit when you draw an image

0
votes

OK, Simon put me on the right path. It looks like browsers treat files from the local file system differently to files from a web server -- specifically, image files are flag as origin-clean = false. This was causing a security exception (NS_ERROR_DOM_SECURITY_ERR 1000) when I tried calling getImageData on the context after having drawn the image.

Solution 1 is to host the files on a web server.

Solution 2 is a bit of a hack but it's fine for my purposes:

try {
    try { 
        var imgd = context.getImageData(0, 0, width, height); 
    } catch (e) { 
    netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
        var imgd = context.getImageData(0, 0, width, height);
    }
} catch (e) {throw new Error("unable to access image data: " + e)}

If a security exception is thrown, the browser will prompt the user for permission to override.