0

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;
Aleksei S.
  • 13
  • 5

1 Answers1

1

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]
millhouse
  • 9,817
  • 4
  • 32
  • 40
  • Oh, no... thank you for the info. But changing the opacity value is not the case for me. Because what I'm doing here is trying to convert big array of numbers (over 2 mln) into a "compressed" base64 string. With the intention to read it later. So the data loss or change is not acceptable in my situation. P.S. For me it's not about the visual images but an alternative way to compress big array of data. – Aleksei S. Feb 23 '22 at 11:54
  • Ah I see @AlekseiS. - well given the above potential lossiness of the `Canvas` implementation, it might not be a practicable way to compress your data array (clever though!) – millhouse Feb 23 '22 at 21:58