I'm trying to convert an array into a base64 string. And then back again. I've made a simple example with a sample array looking like this: [0, 1, 2, 3, 0, 1, 2, 3]
and converting with .toDataURL()
in canvas. But by some reason, when I read base64 string back and apply it to the canvas it returns a different image data: [0, 0, 0, 3, 0, 0, 0, 3]
.
Why this happens?
Example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const array = new Uint8ClampedArray([0, 1, 2, 3, 0, 1, 2, 3]);
const initialImageData = new ImageData(array, 2, 1);
ctx.putImageData(initialImageData, 0, 0);
const dataURL = canvas.toDataURL();
console.log(dataURL); // data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAAAXNSR0IArs4c6QAAAA9JREFUGFdjZGBgYGYAAgAAIQAFoFclWQAAAABJRU5ErkJggg==
const img = new Image();
img.onload = () => {
canvas.width = 2;
canvas.height = 1;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, 2, 1);
console.log(imageData.data); // [0, 0, 0, 3, 0, 0, 0, 3]
}
img.src = dataURL;
Looks like you've been caught by this gotcha in the Canvas
implementation:
Warning: Due to the lossy nature of converting to and from premultiplied alpha color values, pixels that have just been set using putImageData() might be returned to an equivalent getImageData() as different values.
(emphasis mine)
The Uint8ClampedArray
you pass in is in format [r, g, b, a]
and the Alpha (a
) component is extremely important. In your test you've supplied an Alpha of 3
and it appears the browser is choosing to "optimize" the other pixels to 0
.
Try a full-opacity value of a
and it all works fine:
const array = new Uint8ClampedArray([0, 1, 2, 255, 0, 1, 2, 255]);
...
console.log(imageData.data); // [0, 1, 2, 255, 0, 1, 2, 255]