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.

2 Likes

@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.

2 Likes

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).

2 Likes

Hi samgannaway,

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

Thank you for your help !