Can't make imaging.putPixels() to work

I’m trying to put a base64 image ( e.data.value) into a layer. I already tried with bunch of different base64 images in online decoders and the base64 img is fine, however when I try to put that base64 image into a layer using the code under, I just get a small portion of pixels into the layer.

The image I’m trying to put in the layer is exact the same width and height as my document. I tried with several base64 images and still doesn’t work, I’m probably doing something wrong but I cannot find what is it. Here’s my code:

window.addEventListener("message", async (e) => {
  if (e.data.key === "exportedImg") {
    const base64 = e.data.value.split(",")[1];
    const buffer = await base64ToArrayBuffer(base64);
    await putDataIntoLayer(buffer);
    return;
  }
});

async function putDataIntoLayer(buffer) {
  await executeAsModal(async () => {
    try {
      const doc = app.activeDocument;
      const layer = await doc.createLayer();

      const imgData = await imaging.createImageDataFromBuffer(buffer,
        {
          "width": doc.width,
          "height": doc.height,
          "components": 4,
          colorSpace: "RGB"
        });

      await imaging.putPixels(imgData,
        {
          layerID: layer.id,
          imageData: imgData,
        });
    } catch (e) {
      console.log(e);
    }
  })
}

async function base64ToArrayBuffer(base64) {
  try {
    var binaryString = atob(base64);
    console.log(binaryString);
    var bytes = new Uint8Array(binaryString.length);
    for (var i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes;
  } catch (e) {
    console.log(e);
  }
}

I tried googling, here in the forum but I cannot find what is happening, I tried different approaches, always the same. What could be wrong?

I used this decoder ( Base64 to Image | Base64 Decode | Base64 Converter | Base64 ) and works well with all the base64 I throw, however seems that after the atob the array gets shortert, from a length of 6million to 1million characters. Is there something I’m doing wrong?

What’s the component size? Is the target an 8, 16 or 32bit image? And do both, the target document as well as your base64 image contain pixels for an alpha channel (because in your code you currently specify components to be 4, i.e. RGB + Alpha)?

In order to debug this I would attempt the reverse operation and look at the numbers. I.e. request the pixels of the existing document and take a look at the dimensions of the return arrays. Then, as you look at your base64 array I would do the math to see if the size makes sense, e.g. multiply width, height, components and component size (in terms of bytes) together and see if it that matches.

The component size is 4, RGBA for both. The input img is 8bit an the target document is 8bit as well.

I already tried to get the document’s pixels amount and is the same as my incoming img source. Both are 1500px * 1000px * 4 = Uint8Array(6000000).

I also just tried saving the base64 data with this code :confused:

const fs = require('fs');
fs.writeFile("plugin-data:/test.png", buffer);

The code aboce susccesfully saves a png image inside the plugin-data folder, exactly the same image / data I get from my source, however the problem seems to be when parsing the base64 data into the atob? I dunno but I’m thinking as a solution at the moment I can save my base64 img as a .png and then place the image in a new layer and delete the tmp img from the plugin-data folder to free space.

But as a programmer, I won’t sleep until I understand why I cannot make it work with the imaging API.

I hear you! :slight_smile: I would feel the same. To be honest though, what you suggested may not be the worst idea and I wouldn’t be surprised if not more performant, since Photoshop will do the file parsing etc. in C/C++ vs. you copying buffers of pixel data around in Javascript…

P.S. I wonder if you’re hitting a data limit on atob() resulting in data getting truncated. You may want to look into options to directly decode to an array, vs. going via string.

I agree man… I just solved the issue with saving the base64 with FS and loading it and place it in a new layer, the speed is decent and solves what I really need. I wonder the same about atob, I used atob externally and seems it gets the same amount of data as inside the UXP, so I don’t know what happens inside the atob function when converts the base64 into binary.