The "putLayerMask" function cannot be applied to the entire image

I encountered some difficulties while using the imaging API. I wanted to use the “putLayerMask” function, but the generated mask aligned with the selected area only, without being applied to the outer regions of the image.

I conducted the following tests:

  1. Filling the array buffer with 0 results in the mask being applied only within the selected region’s rectangle.
  2. Filling the array buffer with 1, but after exporting it as a PNG, it can be observed that remnants of the background outside the region’s rectangle are still visible.

I also attempted to fill the entire array with 0 and planned to use the “putLayerMask” function with an imageData that consists entirely of 0, and then use a second “putLayerMask” to correct the result. However, Photoshop crashed immediately after executing the “putLayerMask” function with an imageData that is entirely composed of 0. I think this may be a bug?

Here is my code:

async function putLayerMask(layer, imageDataObject, originSize) {
  const componentCount = originSize.width * originSize.height;
  const arrayBuffer = new Uint8Array(componentCount);
  const selectedImageData = await imageDataObject.imageData.getData();

  const selectedBound = {
    top: Math.floor(imageDataObject.sourceBounds.top),
    left: Math.floor(imageDataObject.sourceBounds.left),
    bottom: Math.floor(imageDataObject.sourceBounds.bottom),
    right: Math.floor(imageDataObject.sourceBounds.right),
  };

  for (let i = 0; i < componentCount; i++) {
    arrayBuffer[i] = 1;
  }

  let imageIndex = 0;
  for (let i = selectedBound.top; i < selectedBound.bottom; i++) {
    for (let j = selectedBound.left; j < selectedBound.right; j++) {
      const arrayIndex = i * originSize.width + j;
      arrayBuffer[arrayIndex] = selectedImageData[imageIndex];

      imageIndex++;
    }
  }

  const newImageData = await imaging.createImageDataFromBuffer(arrayBuffer, {
    width: originSize.width,
    height: originSize.height,
    components: 1,
    colorSpace: "Grayscale",
  });

  await imaging.putLayerMask({
    layerID: layer.id,
    imageData: newImageData,
    commandName: `putLayerMask-${layer.id}-empty`
  });
}

Adding some images for reference.

Test 2 : Filling the array buffer with 1, but after exporting it as a PNG, it can be observed that remnants of the background outside the region’s rectangle are still visible.

Adding some images for reference.

Test 2 : Filling the array buffer with 1, but after exporting it as a PNG, it can be observed that remnants of the background outside the region’s rectangle are still visible.

What if you deselect the selection before applying a mask?

@Jarda

Hi Jarda,

I had the same idea, but when I tested the three scenarios after deselecting, I got the same result. I’m not sure if it’s an error caused by how I’m deselecting, so I’m wondering if there are other suggestions for deselecting.

Here is my code. I used “putSelection” to deselect, but the array buffer cannot be entirely filled with 0. So I used 1 instead, and it seems to successfully deselect the area.


async function putLayerMask(layer, imageDataObject, originSize) {
  const componentCount = originSize.width * originSize.height;
  const arrayBuffer = new Uint8Array(componentCount);
  const selectedImageData = await imageDataObject.imageData.getData();

  const selectedBound = {
    top: Math.floor(imageDataObject.sourceBounds.top),
    left: Math.floor(imageDataObject.sourceBounds.left),
    bottom: Math.floor(imageDataObject.sourceBounds.bottom),
    right: Math.floor(imageDataObject.sourceBounds.right),
  };

  for (let i = 0; i < componentCount; i++) {
    arrayBuffer[i] = 1; // TODO: bug, can't set all to 0
  }

  const tempImageData = await imaging.createImageDataFromBuffer(arrayBuffer, {
    width: originSize.width,
    height: originSize.height,
    components: 1,
    colorSpace: "Grayscale",
  });

  await imaging.putSelection({
    documentID: layer._docId,
    imageData: tempImageData,
    replace: true,
    targetBounds: {
      top: 0,
      left: 0,
    },
    commandName: `putSelection-${layer._docId}`,
  });

  for (let i = 0; i < componentCount; i++) {
    arrayBuffer[i] = 0; // TODO: bug, need is 0
  }

  let imageIndex = 0;
  for (let i = selectedBound.top; i < selectedBound.bottom; i++) {
    for (let j = selectedBound.left; j < selectedBound.right; j++) {
      const arrayIndex = i * originSize.width + j;
      arrayBuffer[arrayIndex] = selectedImageData[imageIndex];

      imageIndex++;
    }
  }

  const newImageData = await imaging.createImageDataFromBuffer(arrayBuffer, {
    width: originSize.width,
    height: originSize.height,
    components: 1,
    colorSpace: "Grayscale",
  });

  await imaging.putLayerMask({
    layerID: layer.id,
    imageData: newImageData,
    commandName: `putLayerMask-${layer.id}-empty`,
  });
}

Interesting… I’ll take a look.

@bamdart I made a new doc, filled a new layer, made a lasso selection, and ran the code below in UDT. I didn’t have issue you described. If you could package an image and script that demonstrate it, I’ll take another look.


const imaging = require('photoshop').imaging;

async function putLayerMask(layer, imageDataObject, originSize) {
  const componentCount = originSize.width * originSize.height;
  const arrayBuffer = new Uint8Array(componentCount);
  const selectedImageData = await imageDataObject.imageData.getData();

  const selectedBound = {
    top: Math.floor(imageDataObject.sourceBounds.top),
    left: Math.floor(imageDataObject.sourceBounds.left),
    bottom: Math.floor(imageDataObject.sourceBounds.bottom),
    right: Math.floor(imageDataObject.sourceBounds.right),
  };

  for (let i = 0; i < componentCount; i++) {
    arrayBuffer[i] = 1;
  }

  let imageIndex = 0;
  for (let i = selectedBound.top; i < selectedBound.bottom; i++) {
    for (let j = selectedBound.left; j < selectedBound.right; j++) {
      const arrayIndex = i * originSize.width + j;
      arrayBuffer[arrayIndex] = selectedImageData[imageIndex];

      imageIndex++;
    }
  }

  const newImageData = await imaging.createImageDataFromBuffer(arrayBuffer, {
    width: originSize.width,
    height: originSize.height,
    components: 1,
    colorSpace: "Grayscale",
  });

  await imaging.putLayerMask({
    layerID: layer.id,
    imageData: newImageData,
    commandName: `putLayerMask-${layer.id}-empty`
  });
}


{
  const fn = async () => {
    const sel = await imaging.getSelection({});

    return putLayerMask(
      require('photoshop').app.activeDocument.activeLayers[0],
      sel,
      require('photoshop').app.activeDocument
    )
  }

  require("photoshop").core.executeAsModal(fn, {"commandName": "eam"})
  .then(r=>console.log(r))
  .catch(console.error)


}

@samgannaway

Thank you for your response. If you set all the values of the arrayBuffer in this for loop to 0, the aforementioned issue will occur.

@samgannaway

You can also test by removing this section of code and passing an ImageData with all values set to 0 to the imaging.putLayerMask function. This will cause Photoshop to crash directly in my case.

Yes, we did reproduce the crash. I expect that you will see that fixed in next week’s Beta.

Hello, I would like to inquire if this issue has been resolved and what version number I should upgrade to.

The fix went in after the cut for 24.7, so it will be available in the next release (or now in the Beta build).

Hi samgannaway,

This issue is work on Photoshop 25.0 version. (24.7.1 is not support)

Thank you for your help !