Skip to main content
Known Participant
March 29, 2023
Answered

Cycle through layer masks

  • March 29, 2023
  • 2 replies
  • 1059 views

Hi everyone!
I tried to do what I need through actions and I couldn't. Is there any way to do it via script?
I need to select a layer mask, edit it, and then go to the next one, edit it, and so on. But finding them all and selecting them manually is time consuming. Is there a way to make this selection via script to move on to the next mask? Then it would be easier to add a shortcut to the script or create a new action with a function key.
Thanks!

This topic has been closed for replies.
Correct answer jazz-y

 

const reverseOrder = false; // true - layers pass from top to bottom, false - from bottom to top
var s2t = stringIDToTypeID,
    layerMasks = [];
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasBackgroundLayer'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var offset = executeActionGet(r).getBoolean(p) ? 1 : 0;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var to = executeActionGet(r).getInteger(p);
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('itemIndex'));
r.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
var activeLayer = executeActionGet(r).getInteger(p) - offset;
for (var i = 1; i <= to; i++) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
    r.putIndex(s2t('layer'), i);
    if (executeActionGet(r).getInteger(p) != 13) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasUserMask'));
        r.putIndex(s2t('layer'), i);
        if (executeActionGet(r).getBoolean(p)) layerMasks.push(i)
    }
}
if (reverseOrder) layerMasks.reverse()
var i = 0;
if (layerMasks.length) {
    do {
        if (i == layerMasks.length) { i = 0; activeLayer = reverseOrder ? to + 1 : 0 }
        if (reverseOrder ? layerMasks[i] < activeLayer : layerMasks[i] > activeLayer) { activeLayer = layerMasks[i]; break; }
        i++
    } while (true)
    (r = new ActionReference()).putIndex(s2t("layer"), activeLayer);
    (d = new ActionDescriptor()).putReference(s2t("target"), r)
    executeAction(s2t("select"), d, DialogModes.NO);
    (r = new ActionReference()).putEnumerated(s2t("channel"), s2t('channel'), s2t('mask'));
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    executeAction(s2t('select'), d, DialogModes.NO);
}

 

 

2 replies

jazz-yCorrect answer
Legend
March 29, 2023

 

const reverseOrder = false; // true - layers pass from top to bottom, false - from bottom to top
var s2t = stringIDToTypeID,
    layerMasks = [];
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasBackgroundLayer'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var offset = executeActionGet(r).getBoolean(p) ? 1 : 0;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var to = executeActionGet(r).getInteger(p);
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('itemIndex'));
r.putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
var activeLayer = executeActionGet(r).getInteger(p) - offset;
for (var i = 1; i <= to; i++) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
    r.putIndex(s2t('layer'), i);
    if (executeActionGet(r).getInteger(p) != 13) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('hasUserMask'));
        r.putIndex(s2t('layer'), i);
        if (executeActionGet(r).getBoolean(p)) layerMasks.push(i)
    }
}
if (reverseOrder) layerMasks.reverse()
var i = 0;
if (layerMasks.length) {
    do {
        if (i == layerMasks.length) { i = 0; activeLayer = reverseOrder ? to + 1 : 0 }
        if (reverseOrder ? layerMasks[i] < activeLayer : layerMasks[i] > activeLayer) { activeLayer = layerMasks[i]; break; }
        i++
    } while (true)
    (r = new ActionReference()).putIndex(s2t("layer"), activeLayer);
    (d = new ActionDescriptor()).putReference(s2t("target"), r)
    executeAction(s2t("select"), d, DialogModes.NO);
    (r = new ActionReference()).putEnumerated(s2t("channel"), s2t('channel'), s2t('mask'));
    (d = new ActionDescriptor()).putReference(s2t('target'), r);
    executeAction(s2t('select'), d, DialogModes.NO);
}

 

 

rrprecAuthor
Known Participant
March 29, 2023

Thanks @jazz-y. Worked Perfectly!

Conrad_C
Community Expert
Community Expert
March 29, 2023

See if this helps. I built the action in the picture below, in only two steps. To record the Select Backward Layer step, I used the Option-[ shortcut (Alt-[ in Windows) which steps one layer back (down) in the stack. To record the Select Mask Channel step, I clicked the mask of the current layer.

 

You have to record the first step using the keyboard shortcut, not by clicking a layer. The reason is that if you click a layer, the action will record selecting a layer of a specific name, so that won’t work with any other layers. The keyboard shortcut is relative (next layer back regardless of the name), so it works with any layer stack.

 

That action displays an error if it hits a layer with no mask, like the Background, but you can press the Enter key to continue. You will probably also want to make an action going the other way, for the shortcut that steps to the next layer forward (up) in the stack using the Option/Alt-] shortcut, and selects its mask. Then you could reverse out of hitting the top or bottom of the stack.

 

You probably know this already, but after recordong an action, you can assign a function key to it by double-clicking the action (the shortcut for choosing Action Options from the Actions panel menu).

rrprecAuthor
Known Participant
March 29, 2023

Thanks for the answer @Conrad_C!
Not all layers have masks. Using the backward layer or forward layer commands in the action, the selection will go through all layers, including those without masks. I need to select only the layers that have masks, one by one, to edit the masks.

Conrad_C
Community Expert
Community Expert
March 29, 2023

Yeah, to do that then maybe a script is required. Although the Layers panel has filters, I don’t see one that will show only layers with masks, so maybe that can’t be done in an action.

 

Looks like jazz-y has just posted a script idea, you should probably move on to that (my scripting expertise basically doesn’t exist 🙂 )