Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 canvas getImageData() can't work

When I use the getImageData() function to get the data of a image with chrome, it says that Uncaught IndexSizeError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The source width is 0. Here is my code:

<!DOCTYPE html>
<html>
<head>
    <title>canvastest</title>
</head>
<body>
<img src="flower.jpg" hidden />
<canvas id="drawing" width="600" height="532">a drawing of something</canvas>
<script type="text/javascript" >
    var drawing = document.getElementById("drawing");
    if (drawing.getContext){
        var context = drawing.getContext("2d"),
            image = document.images[0],
            imageData,data,
            i,len,average,
            red,blue,green,alpha;

        image.onload = function(){
            context.drawImage(image,0,0);
        };

        imageData = context.getImageData(0,0,image.width,image.height);
        data = imageData.data;

        for (i=0,len=data.length;i<len;i+=4){
            red = data[i];
            green = data[i+1];
            blue = data[i+2];
            alpha = data[i+3];

            average = Math.floor((red + green + blue) / 3);

            data[i] = average;
            data[i+1] = average;
            data[i+2] = average;
        }

        imageData.data = data;
        context.putImageData(imageData,0,0);
    }
</script>
</body>
</html>

How could it happended? And how to fix it?

like image 431
chensunn Avatar asked Oct 22 '15 12:10

chensunn


2 Answers

You are attaching an onload callback to actually draw the image to the canvas. But that only fires once the image is loaded in the DOM and it has to be fetched from the server. What makes you think any of that happens before you try to pull the image data from the canvas?

That's the problem when mixing synchronous and asynchronous code, and there are numerous techniques for getting around it. But I'd bet money if you set a break point in the debugger at the line the draws the image and the line that tries to read it that the latter will trigger before the former.

You either need to put the processing in your onload handler (not recommended because of the coupling) or use something like a promise to make sure the code that reads the data runs after the code that writes it.

like image 70
Jared Smith Avatar answered Nov 16 '22 06:11

Jared Smith


That error message will also occur if you accidentally pass 0 to the width or height parameter:

context.getImageData(0, 0, 0, 0); //Throws an IndexSizeError

Regardless of whether it was due to a typo or a bad variable, passing 0 to the third or fourth parameter will always result in an error message of "The source width is 0." or "The source height is 0."

So instead of doing that, you should either reference the canvas's width/height directly:

context.getImageData(0, 0, context.canvas.clientWidth, context.canvas.clientHeight);

Or add a condition that checks to make sure that neither value is falsy:

if (my_width && my_height)
{
    context.getImageData(0, 0, my_width, my_height);
}

else
{
    alert('Invalid canvas width or height value!');
}
like image 35
Pikamander2 Avatar answered Nov 16 '22 04:11

Pikamander2