Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transparency lost with getImageData - HTML5 2d Context

I noticed a strange problem with getImageData; transparency of the image seems to be ignored when the image data is fetched.

Since any image needs to be drawn on to a canvas before its image data can be obtained, I assumed this was a problem with the canvas in question being opaque. But I was wrong, since using the canvas as an argument in drawImage maintains transparency.

Here is how I loaded the image;

var load_image = function(name, url, holder, callback) {
    var img = new Image();
    img.src = url;

    img.addEventListener('load', function(e) {
        var canvas = make_canvas(e.target.width, e.target.height);
        var ctx = canvas.getContext('2d');

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(e.target, 0, 0);

        holder[name] = {canvas: canvas, ctx: ctx};

        delete e.target;

        callback.call();
    }, false);
};

The callback is simply the draw function, which invokes draw_image to draw the image.

The usual version;

var draw_image = function(ctx, img, sx, sy, w, h, dx, dy) {
    ctx.drawImage(img.canvas, sx, sy, w, h, dx, dy, w, h);
};

It simply takes the canvas as an argument for drawImage, and the result is as intended with transparency maintained. Example.

The image data version;

var draw_image = function(ctx, img, sx, sy, w, h, dx, dy) {
    var imagedata = img.ctx.getImageData(sx, sy, w, h);
    ctx.putImageData(imagedata, dx, dy);
};

This one obtains the image data of the required rectangle from the same canvas as the one used in the usual version, and puts the image data on the canvas I want to draw to. I believe transparency should be maintained, but this is not so. Example. (This is a Dropbox link because of the origin-clean flag.)

Am I wrong in assuming that transparency should be maintained with getImageData? Or am I just using it in a wrong manner?

Either way, help would really be appreciated.

like image 626
Rikonator Avatar asked Jan 01 '13 13:01

Rikonator


1 Answers

I believe your problem is that putImageData doesn't use a composite operation to blend the source and destination image data. Rather, it's doing a direct write of the red, green, blue, and alpha channels into the canvas. For fully transparent pixels, this means they may or may not appear in the color you expect.

Instead, you can create an intermediate canvas element, draw to that, and then use drawImage() to render that to your target canvas with the appropriate globalCompositeOperation being applied. Mask for putImageData with HTML5 canvas? discusses this issue in more detail.

Update: You can verify that the data you're writing into your "broken" example actually does have transparent data in it by doing ctx.getImageData(230,50,1,1).data. The result is [0,0,0,0] - i.e. a fully transparent pixel.

like image 126
broofa Avatar answered Oct 15 '22 23:10

broofa