Apply Adjustment Layers - JSX

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 11

/*

==========================================
APPLY ADJUSTMENT LAYERS 2.0
==========================================

Cobbled together by Mike McCain


https://lowsunsamurai.com/
Twitter: @mmccain

==========================================

This script applies the currently-selected Adjustment Layer(s) to the appropriate


layers below them.

It works with nested layer groups, clipping masks, and layer masks.

Layers with layer effects on them will be adjusted; however the layer effects
themselves will not.

When any selected Adjustment Layers are also Clipping Masks...


-- they will still get applied to EVERYTHING below it in the PSD (as if they
weren't a clipping mask.)
-- if there are any clipping masks directly above them in the stack, they'll
get released.

Feedback? Let me know at [email protected]

==========================================

*/

app.activeDocument.suspendHistory("Apply Adjustment Layer",


"init(activeDocument)");

function init(ref) {

var savedState = ref.activeHistoryState;

try {
main(ref);
return;

} catch (err) {
ref.activeHistoryState = savedState;
alert("Sorry, script failed (or you canceled it by hitting ESC)");
return;
}
}

function main(ref) {

var prefs = new Object();


prefs.showPanels = false;
prefs.preserveLayers = false;
prefs.processBlendModes = false;
prefs.processHidden = false;
prefs.processLocked = false;
prefs.processSmartObjects = false;
prefs.process3D = false;
prefs.processShapes = false;
prefs.processText = false;

var textStyle = $.os.toLowerCase().indexOf('mac') >= 0 ? "window:10":


"window:11";

var dlg = new Window('dialog', "Apply Adjustment Layer", [500,300,820,783]);

dlg.credit = dlg.add('panel', [10,10,310,78], 'AUTHOR');

dlg.credit.byline = dlg.credit.add('statictext', [16,10,315,22], 'By Mike


McCain — lowsunsamurai.com');
dlg.credit.byline.graphics.font = textStyle;

dlg.credit.byline2 = dlg.credit.add('statictext', [16,23,315,36], 'Instagram


— @mike.mccain.art');
dlg.credit.byline2.graphics.font = textStyle;

dlg.credit.byline3 = dlg.credit.add('statictext', [16,36,315,49], 'Twitter —


@mmccain');
dlg.credit.byline3.graphics.font = textStyle;

dlg.options = dlg.add('panel', [10,82,310,292], 'OPTIONS');


dlg.options.showPanels = dlg.options.add('checkbox', [15,12,315,32], "Show
operations in Layers Panel (slower)");
dlg.options.showPanels.graphics.font = textStyle;
dlg.options.preserveLayers = dlg.options.add('checkbox', [15,32,315,52],
"Don't delete Adjustment Layer(s) when finished");
dlg.options.preserveLayers.graphics.font = textStyle;
dlg.options.processBlendModes = dlg.options.add('checkbox', [15,52,315,72],
"Apply to layers with non-normal blending modes");
dlg.options.processBlendModes.graphics.font = textStyle;
dlg.options.processHidden = dlg.options.add('checkbox', [15,72,315,92],
"Apply to hidden layers");
dlg.options.processHidden.graphics.font = textStyle;
dlg.options.processLocked = dlg.options.add('checkbox', [15,92,315,112],
"Apply to fully-locked layers");
dlg.options.processLocked.graphics.font = textStyle;
dlg.options.processSmartObjects = dlg.options.add('checkbox',
[15,112,315,132], "Apply to Smart Objects (RASTERIZES THEM!)");
dlg.options.processSmartObjects.graphics.font = textStyle;
dlg.options.processShapes = dlg.options.add('checkbox', [15,132,315,152],
"Apply to Shape Layers (RASTERIZES THEM!)");
dlg.options.processShapes.graphics.font = textStyle;
dlg.options.processText = dlg.options.add('checkbox', [15,152,315,172],
"Apply to Text Layers (RASTERIZES THEM!)");
dlg.options.processText.graphics.font = textStyle;
dlg.options.process3D = dlg.options.add('checkbox', [15,172,315,192], "Apply
to 3D Layers (RASTERIZES THEM!)");
dlg.options.process3D.graphics.font = textStyle;
dlg.tip1 = dlg.add('panel', [10,296,310,351], 'PRO TIP');
dlg.tip1.tip = dlg.tip1.add('statictext', [16,10,285,90], 'Select multiple
Adjustment Layers to apply them all at once! They don\'t need to be next to each
other.', {multiline:true});
dlg.tip1.tip.graphics.font = textStyle;

dlg.tip2 = dlg.add('panel', [10,355,310,410], 'NOTE');


dlg.tip2.tip = dlg.tip2.add('statictext', [16,10,285,90], 'If any selected
Adjustment Layers are also Clipping Masks, their Clipping Mask behavior will be
ignored.', {multiline:true});
dlg.tip2.tip.graphics.font = textStyle;

// buttons for ok, and cancel;


dlg.buttons = dlg.add('panel', [10,420,310,470], '');
dlg.buttons.buildBtn = dlg.buttons.add('button', [10,10,145,36], 'Apply',
{name:'ok'});
dlg.buttons.buildBtn.graphics.font = textStyle;
dlg.buttons.cancelBtn = dlg.buttons.add('button', [150,10,285,36], 'Cancel',
{name:'cancel'});
dlg.buttons.cancelBtn.graphics.font = textStyle;

// show the dialog;


dlg.center();

var myReturn = dlg.show ();

if (myReturn == 1) {

// ESTIMATE total number of layers (would take too much running time to
accurately count I think)
var layerCount = 0;
var currentLayers = ref.layers;
var leng = currentLayers.length;
for (var d = leng - 1; d >= 0; d--) {
if (currentLayers[d].typename == 'LayerSet') {
layerCount = layerCount + (currentLayers[d].layers.length)
+ 1;
} else {
layerCount++;
}
}
layerCount = layerCount * 2;

var progressWindow = createProgressWindow("Applying Adjustment


Layer(s)...", undefined, 0, layerCount, undefined, false);
progressWindow.show();
progressWindow.isDone = false;

prefs.showPanels = dlg.options.showPanels.value;
prefs.preserveLayers = dlg.options.preserveLayers.value;
prefs.processBlendModes = dlg.options.processBlendModes.value;
prefs.processHidden = dlg.options.processHidden.value;
prefs.processLocked = dlg.options.processLocked.value;
prefs.processSmartObjects = dlg.options.processSmartObjects.value;
prefs.process3D = dlg.options.process3D.value;
prefs.processShapes = dlg.options.processShapes.value;
prefs.processText = dlg.options.processText.value;

// support applying mulitple adjustment layers at once!

var adj_layers = getSelectedLayers(ref);

// hide and re-show layers and properties panels for speed!

var ar = new ActionReference();


var panels = [];
ar.putProperty( charIDToTypeID("Prpr"),
stringIDToTypeID("panelList") );
ar.putEnumerated( charIDToTypeID("capp"), charIDToTypeID("Ordn"),
charIDToTypeID("Trgt") );
var desc = executeActionGet(ar).getList
(stringIDToTypeID("panelList"));

if (!prefs.showPanels) {

var isPropertiesOpen = false;


var isLayersOpen = false;
var isColorOpen = false;
var isHistogramOpen = false;
var isOptionsOpen = false;
var isChannelsOpen = false;

for(var a = 0;a<desc.count;a++){

if
(desc.getObjectValue(a).getBoolean(stringIDToTypeID("visible")) == true) {

if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Properties") {
isPropertiesOpen = true;

} else if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Layers") {
isLayersOpen = true;

} else if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Color") {
isColorOpen = true;

} else if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Histogram") {
isHistogramOpen = true;

} else if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Options") {
isOptionsOpen = true;

} else if (desc.getObjectValue(a).getString
(stringIDToTypeID("name")) == "Channels") {
isChannelsOpen = true;
}
}
}

if (isPropertiesOpen) {
runMenuItem(app.stringIDToTypeID("togglePropertiesPanel"));
}
if (isLayersOpen) {
runMenuItem(app.stringIDToTypeID("toggleLayersPalette"));
}
if (isColorOpen) {
runMenuItem(app.stringIDToTypeID("toggleColorPalette"));
}
if (isHistogramOpen) {

runMenuItem(app.stringIDToTypeID("toggleHistogramPalette"));
}
if (isOptionsOpen) {
runMenuItem(app.stringIDToTypeID("toggleOptionsPalette"));
}
if (isChannelsOpen) {
runMenuItem(app.stringIDToTypeID("toggleChannelsPalette"));
}
}

// let's do it

applyAdjustment(adj_layers, ref, prefs, progressWindow);

// catch hidden or locked adjustment layers that weren't already tagged

for (var aa in adj_layers) {

if (prefs.preserveLayers) {
adj_layers[aa].name = "APPLIED! " + adj_layers[aa].name;
adj_layers[aa].visible = false;
} else {
adj_layers[aa].allLocked = false;
adj_layers[aa].remove();
}
}

if (!prefs.showPanels) {

if (isPropertiesOpen) {
runMenuItem(app.stringIDToTypeID("togglePropertiesPanel"));
}
if (isLayersOpen) {
runMenuItem(app.stringIDToTypeID("toggleLayersPalette"));
}
if (isColorOpen) {
runMenuItem(app.stringIDToTypeID("toggleColorPalette"));
}
if (isHistogramOpen) {

runMenuItem(app.stringIDToTypeID("toggleHistogramPalette"));
}
if (isOptionsOpen) {
runMenuItem(app.stringIDToTypeID("toggleOptionsPalette"));
}
if (isChannelsOpen) {
runMenuItem(app.stringIDToTypeID("toggleChannelsPalette"));
}
}

return;

// close out if user hit cancel


} else {
return;
}

function applyAdjustment(adj_layers, ref, prefs, progressWindow) {

var len = ref.layers.length;

// start from the bottom of the layer stack and work up


for (var i = len - 1; i >= 0; i--) {
adjust();
progressWindow.updateProgress();
}

function adjust() {
var layer = ref.layers[i];
var isLocked = false;
var isHidden = false;

// flag hidden layer, or ignore if option unchecked


if (!layer.visible) {
if (prefs.processHidden) {
isHidden = true;
} else {
return;
}
}

// flag locked layers, or ignore if option unchecked


if (layer.allLocked) {
if (prefs.processLocked) {
isLocked = true;
} else {
return;
}
}
// unlock the layer if its locked, in order to merge.
if (isLocked) {
layer.allLocked = false;
}

// if it's a hidden layer, show it in order to merge.


if (isHidden) {
layer.visible = true;
}

// recurse if it's a group!


if (layer.typename == 'LayerSet') {

// catch annoying layer groups with their own blend modes


if (layer.blendMode == BlendMode.PASSTHROUGH || layer.blendMode
== BlendMode.NORMAL || prefs.processBlendModes) {

applyAdjustment(adj_layers, layer, prefs, progressWindow);

} else {
return;
}

} else {

// ignore non-normal blend modes if checked


if (layer.blendMode != BlendMode.NORMAL && !
prefs.processBlendModes) {

return;
}

// workaround a bug in older versions of Photoshop CS and CC,


where Color Lookup adjustment layers crash if layer.kind is even invoked on them...
fun
if (isColorLookup(layer)) {
return;
}

// rasterize non-raster layers if checked, or ignore if unchecked


if (
(layer.kind == LayerKind.SMARTOBJECT &&
prefs.processSmartObjects) ||
(layer.kind == LayerKind.LAYER3D && prefs.process3D) ||
(layer.kind == LayerKind.SOLIDFILL && prefs.processShapes)
||
(layer.kind == LayerKind.GRADIENTFILL &&
prefs.processShapes) ||
(layer.kind == LayerKind.PATTERNFILL &&
prefs.processShapes) ||
(layer.kind == LayerKind.TEXT && prefs.processText)
) {
layer.rasterize(RasterizeType.ENTIRELAYER);
}

for (var a in adj_layers) {

var adj_layer = adj_layers[a];


// if the layer IS the adjustment layer, mark it as
processed
if (layer == adj_layer) {

if (prefs.preserveLayers) {

// remove it from the set of adjustment layers


being applied, to save some time.
adj_layers.splice(a, 1);

layer.name = "APPLIED! " + layer.name;


layer.visible = false;
}
}

// if the layer isn't a normal raster layer at this point,


skip and move on
if (layer.kind != LayerKind.NORMAL) {

return;

// make sure the adjustment layer is above the target layer


} else if (layer.itemIndex < adj_layer.itemIndex) {

// mak a dupe of adjustment layer and place it above


target layer
var temp_adjust;
var adj_locked = false;

// handle the case of the adjustment layer being


locked... annoying
if (adj_layer.allLocked) {
adj_locked = true;
adj_layer.allLocked = false;
}

if (layer.isBackgroundLayer) {

temp_adjust = adj_layer.duplicate(layer,
ElementPlacement.PLACEBEFORE);

} else {

// workaround for clipping masks...

temp_adjust = adj_layer.duplicate(layer,
ElementPlacement.PLACEAFTER);
temp_adjust.move(layer,
ElementPlacement.PLACEBEFORE)

}
// ...and apply it down.
temp_adjust.merge();

// re-lock the adjustment layer if needed


if (adj_locked) {
adj_layer.allLocked = true;
}

// DEBUG to see which layers were successfully


applied
// layer.name = "DONE! " + layer.name;

}
}
}

// re-lock the layer if we had to unlock it.


if (isLocked) {
layer.allLocked = true;
}

// re-hide the layer if we had to unhide it.


if (isHidden) {
layer.visible = false;
}

// Helpers to get array of selected layers from Łukasz Wieczorek:


https://gist.github.com/hilukasz/03b17ee78414aadff995

function cTID(s) { return app.charIDToTypeID(s); };


function sTID(s) { return app.stringIDToTypeID(s); };

function newGroupFromLayers(doc) {
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putClass( sTID('layerSection') );
desc.putReference( cTID('null'), ref );
var lref = new ActionReference();
lref.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') );
desc.putReference( cTID('From'), lref);
executeAction( cTID('Mk '), desc, DialogModes.NO );
};

function undo() {
executeAction(cTID("undo", undefined, DialogModes.NO));
};

function getSelectedLayers(doc) {
var selLayers = [];
newGroupFromLayers();

var group = doc.activeLayer;


var layers = group.layers;
var len = layers.length;
for (var i = len - 1; i >= 0; i--) {
selLayers.push(layers[i]);
}

undo();

return selLayers;
};

// Helpers to generate progress bar by Mike Hill: https://www.ps-


scripts.com/viewtopic.php?f=68&t=9732&hilit=user+escape&start=10

function createProgressWindow(title, message, min, max, parent, useCancel) {


var win = new Window('palette', title);
win.bar = win.add('progressbar', undefined, min, max);
win.bar.preferredSize = [300, 20];
//win.stProgress = win.add('statictext');
// win.stProgress .preferredSize.width = 200;
win.parent = undefined;

if (parent) {
if (parent instanceof Window) {
win.parent = parent;
} else if (useCancel == undefined) {
useCancel = parent;
}
}

if (useCancel) {
win.cancel = win.add('button', undefined, 'Cancel');
win.cancel.onClick = function() {
try {
if (win.onCancel) {
var rc = win.onCancel();
if (rc || rc == undefined) {
win.close();
}
} else {
win.close();
}
} catch (e) { alert(e); }
}
}

win.updateProgress = function(val) {
var win = this;
if (val != undefined) {
win.bar.value = val;
}else {
win.bar.value++;
}
if (win.recenter) {
win.center(win.parentWin);
}
win.update();
}
win.center(win.parent);
return win;
};

function isColorLookup(layer){
var ref, desc, adjustmentDesc, layerSectionType;
ref = new ActionReference();
ref.putIdentifier(charIDToTypeID("Lyr "), layer.id);
desc = executeActionGet(ref);
var layerType =
typeIDToStringID(desc.getEnumerationValue(stringIDToTypeID('layerSection')));
if(layerType != 'layerSectionContent') return;
if(desc.hasKey(stringIDToTypeID('adjustment'))){
if (typeIDToStringID(desc.getList(stringIDToTypeID('adjustment')).getClass(0)) ==
'colorLookup') {
return true;
}
} else {
return false;
}
};

You might also like