Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript canvas.toDataURL changes compared to original data URI

I'm currently developing a mozilla extension, trying to load an image (PNG) which is encoded with a data URI, draw it on a canvas element, to finally change some pixels values and save it back as a file on disk.

The strange thing I notice is that even if I don't change anything on the image and I just draw the image on the canvas and the use canvas.toDataURL() to see what is generated, this encoded data is different from the original.

The code I'm using to see this is pretty basic:

var image = new Image();
image.onload = function() {
    var canvas = document.createElement('canvas')
    canvas.width = image.width;
    canvas.height = image.height;
    canvas.getContext('2d').drawImage(image, 0, 0);
    var data = canvas.toDataURL(); // this right here is different from image.scr data!
}
image.src = "data:image/png;base64," + encodedData;

I assume that there's some kind of compression or may be something related to transparencies going on.

I have checked the documentation but couldn't find the explanation. I think I saw something about some colors being changed to some other similar, and that might be what is messing with what I want to achieve (that is changing some pixel values).

Any ideas on what is going on?

If not, does anyone know if there's any other way of saving the canvas image to a file on disk without using toDataURL method? May be a XPCOM component capable of saving all the pixels in PNG binary format?

Thanks

like image 592
sagar38 Avatar asked Nov 05 '22 00:11

sagar38


1 Answers

There is one particular case that I have found where pixels have been changed. There may be more cases. I can not offer an in-depth technical explanation, thus, I can not say that it actually does not change the image.

If you set the alpha value to anything less than 255 (1.0) then the other components for that pixel seem to become altered. A workaround for the problem was to simply set all alpha values to 255.

As of this moment, I am successfully able to store exact precision data into Canvas using ImageData and an Image DOM Element and then retrieve this exact same data with no loss by setting the alpha component to 255 and not using it to store data.

Here are the routines that I am currently using:

function make_image_with_data(contents) {
    // Fractional side length.
    var fside = Math.sqrt(contents.length / 3);
    // Whole integer side length.
    var side = Math.ceil(fside);

    var cv = document.createElement('canvas');

    cv.width = side;
    cv.height = side;

    var ctx = cv.getContext('2d');
    var id = ctx.createImageData(side, side);

    var data = id.data;

    data[0] = (contents.length >> 24) & 0xff;
    data[1] = (contents.length >> 16) & 0xff;
    data[2] = (contents.length >> 8) & 0xff;
    data[3] = 255;
    data[4] = (contents.length) & 0xff;

    var z = 5;
    var x = 0;

    for (; x < contents.length; ++x) {
        data[z] = contents.charCodeAt(x);
        // Skip the image alpha component.
        ++z;
        if (z % 4 == 3) {
            data[z] = 255;
            ++z;
        }
    }

    ctx.putImageData(id, 0, 0);

    var img = document.createElement('img');
    img.width = side;
    img.height = side;

    img.src = cv.toDataURL();

    $(document.body).append(img);

    return img;
}

Then the function to get the data back out is:

function load_data_from_image(img) {
    var cv = document.createElement('canvas');
    cv.width = img.width;
    cv.height = img.height;
    var ctx = cv.getContext('2d');

    ctx.drawImage(img, 0, 0, img.width, img.height);

    var id = ctx.getImageData(0, 0, img.width, img.height);
    var data = id.data;

    var len = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[4];
    var out = new Uint8Array(new ArrayBuffer(len)); 

    var z = 5;

    for (var x = 0; x < len; ++x) {
        out[x] = data[z];
        // This is skipping the alpha component.
        ++z;
        if (z % 4 == 3) {
            ++z;
        }
    }

    return out;
}
like image 172
kmcguire Avatar answered Nov 09 '22 12:11

kmcguire