Layer mask functions

Use this Forum for Beta-Testing your Photoshop Scripts

Moderators: Tom, Kukurykus

Andrew

Layer mask functions

Post by Andrew »

Can you test if a layer has a layer mask.

Can you count how many layers have layer masks and identify the layer names (or index's) that have masks.

Can you delete all layer masks or the layer mask of an individual layer.

I have been having a look at the channels/ channel Objects and it seems layer masks are not part of the collection.

Andrew
xbytor

Layer mask functions

Post by xbytor »

A lot of stuff with layer masks can only be done via ActionDescriptors. Here's s slice of code from a (fixed) version of Getter.jsx that will return the descriptor for a specified layer. I haven't tested it with layerSet nesting but it will likely not work too well.

Code: Select all//====================================================================
//                 Code extracted from Getter.jsx
//====================================================================

Getter = function() {};

Getter.getDocumentIndex = function(doc) {
  var docs = app.documents;
  for (var i = 0; i < docs.length; i++) {
    if (doc == docs) {
      return i+1;
    }
  }
  return undefined;
};

Getter.getDescriptorElement = function(desc, key) {
  if (!desc.hasKey(key)) {
    return undefined
  }
  var typeID = desc.getType(key);
  var v;
  switch (typeID) {
    case DescValueType.ALIASTYPE:   v = desc.getPath(key); break;
    case DescValueType.BOOLEANTYPE: v = desc.getBoolean(key); break;
    case DescValueType.CLASSTYPE:   v = desc.getClass(key); break;
    case DescValueType.DOUBLETYPE:  v = desc.getDouble(key); break;
    case DescValueType.ENUMERATEDTYPE:
      v = new Object();
      v.type = desc.getEnumerationType(key);
      v.value = desc.getEnumerationValue(key);
      break;
    case DescValueType.INTEGERTYPE: v = desc.getInteger(key); break;
    case DescValueType.LISTTYPE:    v = desc.getList(key); break;
    case DescValueType.OBJECTTYPE:
      v = new Object();
      v.type = desc.getObjectType(key);
      v.value = desc.getObjectValue(key);
      break;
    case DescValueType.RAWTYPE:       v = desc.getData(key); break;
    case DescValueType.REFERENCETYPE: v = desc.getReference(key); break;
    case DescValueType.STRINGTYPE:    v = desc.getString(key); break;
    case DescValueType.UNITDOUBLETYPE:
      v = new Object();
      v.type = desc.getUnitDoubleType(key);
      v.value = desc.getUnitDoubleValue(key);
      break;
  }

  return v;
};

Getter.getInfoByIndexIndex = function(childIndex, parentIndex, childID,
                                      parentID, key) {
  var ref = new ActionReference();
  if (isNaN(childID)) {
    childID = cTID(childID);
  }
  if (isNaN(parentID)) {
    parentID = cTID(parentID);
  }
  if (key != undefined) {
    if (isNaN(key)) {
      key = cTID(key);
    }
    ref.putProperty(PSClass.Property, key);
  }
  ref.putIndex(childID, childIndex);
  ref.putIndex(parentID, parentIndex);

  var desc = app.executeActionGet(ref);

  return (key ? Getter.getDescriptorElement(desc, key) : desc);
};

Getter.getLayerDescriptor = function(doc, layer) {
  var docIndex = Getter.getDocumentIndex(doc);
  var layerIndex = -1;
  var layers = doc.layers;

  var hasBackground = false;
  try {
    doc.backgroundLayer;
    hasBackground = true;
  } catch (e) {
  }

  for (var i = 0; i < layers.length; i++) {
    if (layer == layers) {
      layerIndex = layers.length - i;
      if (hasBackground) layerIndex--;
      break;
    }
  }
  if (layerIndex == -1) {
    throw "Unable to find indicated layer in the document.";
  }

  return Getter.getInfoByIndexIndex(layerIndex, docIndex, PSClass.Layer,
                                    PSClass.Document);
};

This could probably be reduced down a bit and I suspect that Mike may have a simplified version.

With this code in place, you can implement a hasMask function like this:

Code: Select all//
// usage:
//   msk = hasLayerMask(doc, doc.activeLayer);
//
function hasLayerMask(doc, layer) {
    var ldesc = Getter.getLayerDescriptor(doc, layer);
    return ldesc.hasKey(cTID('UsrM')) && ldesc.getBoolean(cTID('UsrM'));
};

With this code, it's easy enough to iterate over the layers in a doc and figure out which ones have a mask. Again, I don't know if this will work with layerSets.

BTW, checking to see if the mask is linked should work similarly:
Code: Select allfunction hasLayerMask(doc, layer) {
    var ldesc = Getter.getLayerDescriptor(doc, layer);
    return ldesc.hasKey(cTID('Usrs')) && ldesc.getBoolean(cTID('Usrs'));
};

I haven't tested this, mind you, but this should work according to an XML dump of a layer's ActionDescriptor I'm looking at.

To delete a mask, you first have to select the mask:Code: Select allStdlib.selectLayerMask = function(doc, layer) {
  function _ftn() {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();

    ref.putEnumerated(cTID("Chnl"), cTID("Chnl"), cTID("Msk "));
    desc.putReference(cTID("null"), ref);
    desc.putBoolean(cTID("MkVs"), false );
    executeAction(cTID("slct"), desc, DialogModes.NO );
  }

  Stdlib.wrapLCLayer(doc, layer, _ftn);
};

and then delete it:
Code: Select allfunction deleteLayerMask() {
    var desc7 = new ActionDescriptor();
        var ref6 = new ActionReference();
        ref6.putEnumerated( PSClass.Channel, PSType.Ordinal, PSEnum.Target );
    desc7.putReference( cTID('null'), ref6 );
    executeAction( PSEvent.Delete, desc7, DialogModes.NO );
};

To the best of my knowledge, you can only delete one mask at a time.

Sorry about the inconsistencies of the ID and code formatting. Some of this I had in stdlib.js, some came from code I wrote while consulting, and some I just generated via SL and ran through SLFixer.

-X
Mike Hale

Layer mask functions

Post by Mike Hale »

Like X,

I think the only way you can deal with layer masks is one layer at a time. To delete, I loop through artlayers, test if there is a mask then delete the ones I find as I go. Otherwise I push either the index or name into an array.

BTW, you have to do the same looping when working with vector masks. But at least once the layer is selected a vector mask is part of pathItems and you don't need SL code to deal with it.
Andrew

Layer mask functions

Post by Andrew »

Thanks for that, it will do the job nicely

Andrew
Andrew

Layer mask functions

Post by Andrew »

In X's code above, how can I submit a layer in a layer set. I have followed the code through and it is submitting the doc.layers index as the first parameter to

Getter.getInfoByIndexIndex = function(childIndex, parentIndex, childID,
parentID, key)

so if I want to submit something that is not in the layers Object eg a layer within a layerset, how can I do it?

I am also messing around with alternative ways of finding if a layer has a mask eg using SL code to 'select' the mask and try catch to control the resulting response - same as X's

Code: Select allvar hasBackground = false;
  try {
    doc.backgroundLayer;
    hasBackground = true;
  } catch (e) {
  }

Andrew
xbytor

Layer mask functions

Post by xbytor »

There is a way to have an exception thrown if there is no mask. I'll dig through my code and see if I can find it.

-X