Copy link to clipboard
Copied
I've been a professional editor/retoucher for photographers for the past three years. I use a lot of actions, but I've never branched into scripts until now. I'm trying to refine my workflow and scripts seem like the next logical step.
I do a lot of interiors work, which is basically manual HDR. Here's my current build process, which I'd love to find a way to script. I tried to be very clear, but let me know if this needs fleshing out. I'm using Photoshop CS5.
1. Pull up bracketed, color-corrected TIFFs in Bridge.
2. Re-order as needed, with main exposure at the bottom. Click "Tools" > "Photoshop" > "Load as Photoshop layers" to get a PSD with layers in correct order and main exposure as background (base) layer.
3. Manually go through each layer and add a Hide All layer mask to every layer except the background layer.
4. Save as a PSD, using the original name of the background layer as the filename. To do this I just copy the layer name (example.tiff), paste in the Save dialog and then use the dropdown menu to select PSD (thus saving as example.psd).
I'm hoping to automate steps 3 and 4 as they are the most time consuming for me. Any input is appreciated!!
Thanks again.
Yes your change is good. That way the last layer won't have a mask applied. I wasn't sure if you wanted that, but put in the check for the background layer.
As far as saving, what file format do you want to save to? Here's how I save a psd:
...
var docRef = activeDocument
var doneFolder = new Folder('/c/photos/')
var layerName = docRef.activeLayer.name
var psdOptions = new PhotoshopSaveOptions();
psdOptions.layers = true;
app.displayDialogs = DialogModes.ALL;//include this line if you want the dialog
Copy link to clipboard
Copied
You can try this. Just set a snapshop first incase it does something funky that I didn't catch. I should note that this will only apply the mask to layers that are not in a group.
#target photoshop
if(app.documents.length>0){
var docRef = activeDocument;
var layerNum = docRef.layers.length;
for(var i=0;i<layerNum;i++){
docRef.activeLayer = docRef.layers[i];
if(!docRef.activeLayer.isBackgroundLayer){
try{addMask ()}
catch(e){}
}
}
}
else{alert('There are no open files')};
function addMask(){
var idMk = charIDToTypeID( "Mk " );
var desc2 = new ActionDescriptor();
var idNw = charIDToTypeID( "Nw " );
var idChnl = charIDToTypeID( "Chnl" );
desc2.putClass( idNw, idChnl );
var idAt = charIDToTypeID( "At " );
var ref1 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idMsk = charIDToTypeID( "Msk " );
ref1.putEnumerated( idChnl, idChnl, idMsk );
desc2.putReference( idAt, ref1 );
var idUsng = charIDToTypeID( "Usng" );
var idUsrM = charIDToTypeID( "UsrM" );
var idHdAl = charIDToTypeID( "HdAl" );
desc2.putEnumerated( idUsng, idUsrM, idHdAl );
executeAction( idMk, desc2, DialogModes.NO );
}
Copy link to clipboard
Copied
Thanks so much csuebele. Your exact script was putting a layer mask on every layer.
I brought it into ExtendScript Toolkit and changed line 7
for(var i=0;i<layerNum;i++){
to this:
for(var i=0;i<layerNum-1;i++){
This did the trick as far as layer masking...do you know how to add the save as function into the script? I now know that I can select the correct layer using docRef.activeLayer = docRef.layers[layerNum-1];
but I don't know how to implement saving using that layer name. I'll start digging through the scripting guide now...
Copy link to clipboard
Copied
Yes your change is good. That way the last layer won't have a mask applied. I wasn't sure if you wanted that, but put in the check for the background layer.
As far as saving, what file format do you want to save to? Here's how I save a psd:
var docRef = activeDocument
var doneFolder = new Folder('/c/photos/')
var layerName = docRef.activeLayer.name
var psdOptions = new PhotoshopSaveOptions();
psdOptions.layers = true;
app.displayDialogs = DialogModes.ALL;//include this line if you want the dialog box to come up.
docRef.saveAs (new File(doneFolder +'/' + layerName + '.psd'), psdOptions);
app.displayDialogs = DialogModes.NO;//Resets the ALL - you don't want to leave that on.
Copy link to clipboard
Copied
This is great! Thanks so much. I tweaked it a bit for my needs and ended up with this.
var docRef = activeDocument
docRef.activeLayer = docRef.layers[layerNum-1];
var layerName = docRef.activeLayer.name
var psdOptions = new PhotoshopSaveOptions();
psdOptions.layers = true;
docRef.saveAs (new File(decodeURI(app.activeDocument.path) + '/' + layerName + '.psd'), psdOptions);
app.displayDialogs = DialogModes.NO;
Thanks again!!
Copy link to clipboard
Copied
i know this script is a bit old, but i'm hoping to modify it to copy the mask from the current active layer to all other layers (they are all art layers if that makes a difference). I would really appreciate any direction, i can muddle along with even general ideas
thanks
Brian
Copy link to clipboard
Copied
If you want to use the same layer mask on all the layers I would simply target all the layer above the background layer and create a new layer group from layers and add a single layer mask to that new layer group. you can load the active layer mask you want to use as a selection and add the active selection as the layer group's layer mask. That way existing layer mask will not be replace and lost. They can be deactivated if need be.
Copy link to clipboard
Copied
thanks for the reply - I am pretty sure i have to iterate through the layers and add them to each. I'm masking to just a portion of the image for auto aligning them at a later step. I don't think a layer group mask will do what I need. And afterwards I'll iterate through and delete them all, but if I get the first one that shouldn't be too hard.
but I will certainly play with that
THanks again
Brian
Copy link to clipboard
Copied
How Do I change the mask to a white mask instead of a black mask?
Copy link to clipboard
Copied
A little late to the party, however here is the altered code to create a reveal all mask rather than a hide all mask:
#target photoshop
if (app.documents.length > 0) {
var docRef = activeDocument;
var layerNum = docRef.layers.length;
for (var i = 0; i < layerNum; i++) {
docRef.activeLayer = docRef.layers[i];
if (!docRef.activeLayer.isBackgroundLayer) {
try {
addMask()
} catch (e) {}
}
}
} else {
alert('There are no open files')
};
function addMask() {
var idMk = charIDToTypeID("Mk ");
var desc2 = new ActionDescriptor();
var idNw = charIDToTypeID("Nw ");
var idChnl = charIDToTypeID("Chnl");
desc2.putClass(idNw, idChnl);
var idAt = charIDToTypeID("At ");
var ref1 = new ActionReference();
var idChnl = charIDToTypeID("Chnl");
var idChnl = charIDToTypeID("Chnl");
var idMsk = charIDToTypeID("Msk ");
ref1.putEnumerated(idChnl, idChnl, idMsk);
desc2.putReference(idAt, ref1);
var idUsng = charIDToTypeID("Usng");
var idUsrM = charIDToTypeID("UsrM");
var idRvlA = charIDToTypeID("RvlA");
desc2.putEnumerated(idUsng, idUsrM, idRvlA);
executeAction(idMk, desc2, DialogModes.NO);
}
Copy link to clipboard
Copied
I am late to the party too...
You helped me a lot. Thank you!
Copy link to clipboard
Copied
What was your problem?
Copy link to clipboard
Copied
Hi, @Kukurykus . Thank you for your reply.
I was trying to create the layer mask for the selected layer. Following the Chuck Uebele's answer, I was able to create the black mask, but I wanted a white one.
I am just practicing a little bit of scripting and I did not understand completely the addMask() function in his post.
It would be necessary to dive into it to comprehend. Stephen_A_Marsh's answer solved it quickly. Reading the code again, it looks like the key was this line:
var idRvlA = charIDToTypeID("RvlA");
I still have a question, though:
Why, when the script runs, I am not able to affect the Background layer? In my script, I am NOT using the block below:
if (!docRef.activeLayer.isBackgroundLayer) {
...
}
Through the interface, I can duplicate (the Background layer) and create a layer mask, but not via ExtendScript. The script fails silently...
Do you have any ideas?
Copy link to clipboard
Copied
docRef.layers ==> docRef.layers[i]
Copy link to clipboard
Copied
Hello, @r-bin .
My script is as follows:
#target photoshop
//@include ./lib/clear-console.jsx;
//@include ./lib/get-selected-layers.jsx;
//@include ./lib/add-mask.jsx;
function main() {
// Clears the console window
clearConsole();
var docRef = app.activeDocument;
// Returns an array of the selected layers
var selectedLayers = getSelectedLayers(docRef);
for (i = 0; i < selectedLayers.length; i++) {
// The next layer in the array becomes the active layer.
docRef.activeLayer = selectedLayers[i];
// Duplicates the active layer.
// Does not work with the Background (locked) layer.
var newLayer = docRef.activeLayer.duplicate();
docRef.activeLayer = newLayer;
addMask(docRef);
}
}
main();
My references are:
For the clearConsole() function
Clearing the Adobe ExtendScript ToolKit Console Window
For the getSelectedLayers() function
I believe I am considering the
docRef.layers ==> docRef.layers[i]
By @r-bin
with this line already
docRef.activeLayer = selectedLayers[i];
or am I interpreting it wrong?
Copy link to clipboard
Copied
Copy link to clipboard
Copied
First of all, thank you for your reply.
I should have debbuged the script before asking. I would have known that my getSelectedLayers() function was returning an empty array. I tested with an image with only the Background, and (I believe that) as the Background is locked, it is not selected (although, in the GUI, it appears to be). So, the array was empty.
Having said that, your sugestion (the try-catch block) could not be tested. For some reason, the ExtendScript Debugger extension for VS Code kept crashing on me. I have just downloaded the ExtendScript Toolkit editor and I will try it later.
Copy link to clipboard
Copied
I edited original and later codes of lacking [i] part, so now they work as should.
The background block change to: addMask()
Copy link to clipboard
Copied
@Kukurykus, thank you very much. The script now works as expected.
My intention originally was to duplicate the Background locked (or any selected layer or layers) and add a white mask to the duplicate.
As I could not duplicate or add a mask to the Background, I asked you about any ideas. Now my code is as follows and works as expected.
#target photoshop
//@include ./lib/clear-console.jsx;
//@include ./lib/add-mask.jsx;
function main() {
// Clears the console window
clearConsole();
var docRef = app.activeDocument;
// Limits the loop below (avoiding an infinit loop)
var originalLayerQuantity = docRef.layers.length;
for (i = 0; i < originalLayerQuantity; i++) {
// Defines the active layer
docRef.activeLayer = docRef.layers[i];
// Duplicates the layer (now it works with the Background locked layer too).
var newLayer = docRef.activeLayer.duplicate();
// The duplicate becomes the selected one.
docRef.activeLayer = newLayer
// The mask is added. (I tested with the Background locked layer and it works as well).
addMask();
}
}
main();
Copy link to clipboard
Copied
I added some quick hacks to another script:
/*
Apply Layer Mask to Numerous Layers(without grouping)
https://community.adobe.com/t5/photoshop/apply-layer-mask-to-numerous-layers-without-grouping/m-p/11115417
// apply active selection as layer masks to selected layers;
// 2020, use it at your own risk;
*/
if (app.documents.length != 0) {
////// 2021 hack //////
////// select all layers, which ignores the background layer //////
app.runMenuItem(stringIDToTypeID("selectAllLayers"));
////// select all //////
app.activeDocument.selection.selectAll();
////// 2021 hack //////
applyLayerMasksToLayers();
};
////// apply layer masks to selected layers //////
function applyLayerMasksToLayers() {
var theLayers = getSelectedLayersIdx();
var selection = hasSelection();
if (selection == true) {
app.activeDocument.selection.deselect()
};
for (i = 0; i < theLayers.length; i++) {
selectLayerByIndex(theLayers[i], false);
// remove existing mask;
if (hasLayerMask(theLayers[i]) == true) {
var desc8 = new ActionDescriptor();
var ref5 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
ref5.putEnumerated(idchannel, idchannel, stringIDToTypeID("mask"));
desc8.putReference(stringIDToTypeID("null"), ref5);
executeAction(stringIDToTypeID("delete"), desc8, DialogModes.NO);
};
// load selection;
reselect();
// apply layer mask;
if (selection == true) {
makeLayerMask('RvlS')
} else {
makeLayerMask('RvlA') // or 'HdAl'
};
}
};
////// by paul mr //////
function getSelectedLayersIdx() {
var selectedLayers = new Array;
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var desc = executeActionGet(ref);
if (desc.hasKey(stringIDToTypeID('targetLayers'))) {
desc = desc.getList(stringIDToTypeID('targetLayers'));
var c = desc.count
var selectedLayers = new Array();
for (var i = 0; i < c; i++) {
try {
activeDocument.backgroundLayer;
selectedLayers.push(desc.getReference(i).getIndex());
} catch (e) {
selectedLayers.push(desc.getReference(i).getIndex() + 1);
}
}
} else {
var ref = new ActionReference();
ref.putProperty(charIDToTypeID("Prpr"), charIDToTypeID("ItmI"));
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
try {
activeDocument.backgroundLayer;
selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID("ItmI")) - 1);
} catch (e) {
selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID("ItmI")));
}
}
return selectedLayers;
};
////// by mike hale, via paul riggott //////
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
try {
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
} catch (e) {
alert(e.message);
}
};
////// add layer mask //////
function makeLayerMask(maskType) {
try {
if (maskType == undefined) maskType = 'RvlA'; // or 'HdAl'
// =======================================================
var idMk = charIDToTypeID("Mk ");
var desc3 = new ActionDescriptor();
var idNw = charIDToTypeID("Nw ");
var idChnl = charIDToTypeID("Chnl");
desc3.putClass(idNw, idChnl);
var idAt = charIDToTypeID("At ");
var ref1 = new ActionReference();
var idChnl = charIDToTypeID("Chnl");
var idChnl = charIDToTypeID("Chnl");
var idMsk = charIDToTypeID("Msk ");
ref1.putEnumerated(idChnl, idChnl, idMsk);
desc3.putReference(idAt, ref1);
var idUsng = charIDToTypeID("Usng");
var idUsrM = charIDToTypeID("UsrM");
var idRvlA = charIDToTypeID(maskType);
desc3.putEnumerated(idUsng, idUsrM, idRvlA); // or 'HdAl'
executeAction(idMk, desc3, DialogModes.NO);
} catch (e) {
return false
};
};
////// check for selection //////
function hasSelection() {
var ref10 = new ActionReference();
ref10.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
ref10.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var docDesc = executeActionGet(ref10);
return docDesc.hasKey(stringIDToTypeID("selection"));
};
////// reselect //////
function reselect() {
try {
var idsetd = charIDToTypeID("setd");
var desc7 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
var ref2 = new ActionReference();
var idChnl = charIDToTypeID("Chnl");
var idfsel = charIDToTypeID("fsel");
ref2.putProperty(idChnl, idfsel);
desc7.putReference(idnull, ref2);
var idT = charIDToTypeID("T ");
var idOrdn = charIDToTypeID("Ordn");
var idPrvs = charIDToTypeID("Prvs");
desc7.putEnumerated(idT, idOrdn, idPrvs);
executeAction(idsetd, desc7, DialogModes.NO);
} catch (e) {
return false
}
};
////// has layer mask //////
function hasLayerMask(theIndex) {
var m_Dsc01, m_Ref01;
m_Ref01 = new ActionReference();
m_Ref01.putIndex(charIDToTypeID("Lyr "), theIndex);
//m_Ref01.putEnumerated(sTID("layer"), cTID("Ordn"), cTID("Trgt"));
m_Dsc01 = executeActionGet(m_Ref01);
return m_Dsc01.hasKey(charIDToTypeID("Usrs"));
};
Copy link to clipboard
Copied
And another hack:
/*
Based on: https://community.adobe.com/t5/photoshop-ecosystem/is-there-a-way-to-resize-the-height-of-multiple-images-at-once-without-setting-a-specific-width/td-p/9854232
*/
function sTT(v) {
return stringIDToTypeID(v);
}
(ref = new ActionReference()).putEnumerated
(sTT('document'), sTT('ordinal'), sTT('targetEnum'));
len = executeActionGet(ref).getInteger(sTT('numberOfLayers'));
for (i = 1; i <= len;) {
(ref = new ActionReference()).putIndex(sTT('layer'), i++);
if (typeIDToStringID((dsc = executeActionGet(ref))
.getEnumerationValue(sTT(lS = 'layerSection'))) == lS + 'Content') {
(lst = new ActionList()).putReference(ref), dsc.putList(sTT('null'), lst);
if (!dsc.getBoolean(sTT('visible'))) {
executeAction(sTT('show'), dsc, DialogModes.NO);
}
(ref = new ActionReference()).putIndex(sTT('layer'), i - 1);
(dsc = new ActionDescriptor()).putReference(sTT('null'), ref);
executeAction(sTT('select'), dsc, DialogModes.NO);
with (activeDocument.activeLayer) {
layerMask();
}
}
}
function layerMask() {
var idmake = stringIDToTypeID("make");
var desc6898 = new ActionDescriptor();
var idnew = stringIDToTypeID("new");
var idchannel = stringIDToTypeID("channel");
desc6898.putClass(idnew, idchannel);
var idat = stringIDToTypeID("at");
var ref412 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
var idchannel = stringIDToTypeID("channel");
var idmask = stringIDToTypeID("mask");
ref412.putEnumerated(idchannel, idchannel, idmask);
desc6898.putReference(idat, ref412);
var idusing = stringIDToTypeID("using");
var iduserMaskEnabled = stringIDToTypeID("userMaskEnabled");
var idrevealAll = stringIDToTypeID("revealAll"); // ("hideAll")
desc6898.putEnumerated(idusing, iduserMaskEnabled, idrevealAll); // idhideAll
executeAction(idmake, desc6898, DialogModes.NO);
}
Copy link to clipboard
Copied
I can't seem to get the mask to be applied to the active layer - it only applies the mask to the layer that is highlighted in Photoshop itself.
For example, if the cursor is on "Group 1" any of the functions makeLayerMask() or layerMask () apply a mask to Group 1 even if I set the active layer to "FOO" inside Group 1 with this:
var lyr = activeDocument.layers;
var lyrAct = activeDocument.activeLayer;
lyrAct = lyr.getByName("Group 1").layers.getByName("FOO");
layerMask ();
Any ideas what I'm doing wrong?
Copy link to clipboard
Copied
@Th:F – It might be helpful to post full code and a screenshot of the layers panel to illustrate.
Copy link to clipboard
Copied
Hey @Stephen_A_Marsh here's the code and a quick sketch file I have set up to test it.
#target photoshop
var lyr = activeDocument.layers;
var lyrAct = activeDocument.activeLayer;
lyrAct = lyr.getByName("Group 1").layers.getByName("FOO");
layerMask ();
function layerMask() {
var idmake = stringIDToTypeID("make");
var desc6898 = new ActionDescriptor();
var idnew = stringIDToTypeID("new");
var idchannel = stringIDToTypeID("channel");
desc6898.putClass(idnew, idchannel);
var idat = stringIDToTypeID("at");
var ref412 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
var idchannel = stringIDToTypeID("channel");
var idmask = stringIDToTypeID("mask");
ref412.putEnumerated(idchannel, idchannel, idmask);
desc6898.putReference(idat, ref412);
var idusing = stringIDToTypeID("using");
var iduserMaskEnabled = stringIDToTypeID("userMaskEnabled");
var idrevealAll = stringIDToTypeID("revealAll"); // ("hideAll")
desc6898.putEnumerated(idusing, iduserMaskEnabled, idrevealAll); // idhideAll
executeAction(idmake, desc6898, DialogModes.NO);
};
In Photoshop, a mask will be added to layer "FOO" if the script is run when the layer is selected in the layer palette like this:
However the script won't do anything if Group 1 is selected in the layers palette like this:
The super simple test psd is attached.
Copy link to clipboard
Copied
Is this what you are looking for?
activeDocument.activeLayer = activeDocument.layerSets["Group 1"].layers["FOO"];
Or is that too specific?
Top-level layers or groups and the content of a group are different things. Photoshop doesn't know what "child" items (layers or groups and nested groups/layers etc) are under the top-level "parent" group without recursing through the group.