Copy link to clipboard
Copied
Hi all!
I know to get all layers and groups with a recursive function, but it's slow. It's possible get the same with the ActionRefencen() code? (I think it will be faster).
Thnx!
Copy link to clipboard
Copied
These links may help you..
ps-scripts.com • View topic - One way of getting layers from a layerset with AM
http://www.ps-bridge-scripts.talktalk.net/
Check out the snippets section.
Copy link to clipboard
Copied
// thanks to mike hale and paul riggott;
// 2015, use it at your own risk;
#target "photoshop-70.032"
if (app.documents.length > 0) {
app.activeDocument.suspendHistory("replace", "main ()");
};
////////////////////////////////////
function main () {
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
//if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
theLayers.push([theName, theID])
//};
}
catch (e) {};
};
alert (theLayers.join("\n"));
// based on code by mike hale, via paul riggott;
function selectLayerByID(id,add){
add = undefined ? add = false:add
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID("Lyr "), id);
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);
}
};
Copy link to clipboard
Copied
Thanks you very much! it's really really fast!
Anyway I want the layer's objects instead layer's name or his id, I suppose I can set activeLayer with that ID and 'push' the activeLayer into an array, but I think that will slow down the process.
with:
theLayers.push(setActiveLayerByID(theID));
instead of:
theLayers.push([theName, theID]);
unless exists a way with action manager
var theObject = layerDesc.getObject(bla bla bla...)
Thanks!!!
Copy link to clipboard
Copied
There is a function called "selectLayerByID" in the code with which you could select Layers as needed by their indices, but as you already concluded applying that for all Layers would probably be a considerable drag performance-wise.
Copy link to clipboard
Copied
What he said. The performance penalty in recursing to collect the layer objects instead of IDs is the creation of the layer objects, not in the recursion itself.
Copy link to clipboard
Copied
If the penalty is the object creation, I prefer the typical recursive function (without AM) in favour of clarity
What do you think?
Thanks so much!
Copy link to clipboard
Copied
What do you think?
Unless I'm dealing with 50+ layers or actually need the objects, the recursive Layer object method is what I use.
Below is how I do it in xtools/xlib/stdlib.js
//
// Traverse the all layers, including nested layers, executing
// the specified function. Traversal can happen in both directions.
//
Stdlib.traverseLayers = function(doc, ftn, reverse, layerSets) {function _traverse(doc, layers, ftn, reverse, layerSets) {
var ok = true;
for (var i = 1; i <= layers.length && ok != false; i++) {
var index = (reverse == true) ? layers.length-i : i - 1;
var layer = layers[index];if (layer.typename == "LayerSet") {
if (layerSets) {
ok = ftn(doc, layer);
}
if (ok) {
ok = _traverse(doc, layer.layers, ftn, reverse, layerSets);
}
} else {
ok = ftn(doc, layer);
try {
if (app.activeDocument != doc) {
app.activeDocument = doc;
}
} catch (e) {
}
}
}
return ok;
};return _traverse(doc, doc.layers, ftn, reverse, layerSets);
};Stdlib.getLayersList = function(doc, reverse, layerSets) {
function _ftn(doc, layer) {
_ftn.list.push(layer);
return true;
};_ftn.list = [];
Stdlib.traverseLayers(doc, _ftn, reverse, layerSets);var lst = _ftn.list;
_ftn.list = undefined;
return lst;
};
Copy link to clipboard
Copied
"but it's slow"
I've always wanted to pursue this based on facts, I have some timing tests, but I can tell you a few things.
#1: The Photoshop DOM (docLayers.length) does not cache or track state. That is as designed. When you ask a Photoshop document for the layer count, it actually goes and checks the layers count, this is even worse when you are dealing with layer groups. You want this var a = activeDocument.layers.length; for (var i = 0; i < a; i ++) { ... } and NOT this for (var i = 0; i < activeDocument.layers.length; i ++) { ... }
#2: If you go with the Action* routines and do not use the DOM to solve the above problem DO NOT ASK FOR ALL KEYS. In the below code, if I_like_to_go_fast is true then we ask only for the "tool" information from the application. You can see results by timing this once with true and once with false. You MUST use the true case first or you must restart Photoshop to get proper timing and it depends on what property you are asking for. But always ask for one property at a time to avoid initializing code you don't need. Pulling in all the text information on a layer is the best example. In older versions we had a huge hit when you started up the video libraries we needed.
var ref = new ActionReference();
if (I_like_to_go_fast) {
var keyTool = stringIDToTypeID( "tool" );
var classProperty = charIDToTypeID( "Prpr" );
ref.putProperty( classProperty, keyTool );
}
ref.putEnumerated( charIDToTypeID("capp"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
I use this Object for timing. var t = new Timer(); ... myLogFunction("Some interesting timing: " + t.getElapsed()); Remember you are altering the timing by monitoring the timing! But bottom line is you need to measure it before you can improve it.
// Library for timing things in JavaScript
function Timer() {
// member properties
this.startTime = new Date();
this.endTime = new Date();
// member methods
// reset the start time to now
this.start = function () { this.startTime = new Date(); }
// reset the end time to now
this.stop = function () { this.endTime = new Date(); }
// get the difference in seconds between start and stop
this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000; }
// get the current elapsed time from start to now, this sets the endTime, in seconds
this.getElapsed = function () { this.stop(); return this.getTime(); }
}
Copy link to clipboard
Copied
The Photoshop DOM (docLayers.length) does not cache or track state
Thanks for this, Tom. I'll get my code modified to reflect this. I've learned my one knew fact for the day.
Copy link to clipboard
Copied
But always ask for one property at a time to avoid initializing code you don't need.
Is it even possible to ask for more than one property that way?
Copy link to clipboard
Copied
I haven't been able to get more than one property myself (say the "tool" and the "serialString"), or nested properties (e.g. a descriptor within a descriptor); any trick to perform the magic or it's just not possible?
Davide
Copy link to clipboard
Copied
I hope someday we'll have a full (and fast) API, without using AM.
Meanwhile.. any web, docs, vídeos etc. to learn the basisc of AM?
Thanks!