5 Replies Latest reply on Aug 9, 2014 2:57 AM by matias.kiviniemi

    Looping through layers is slow.

    danieln1993

      Hi, so i am trying to make a script that goes goes through all the layers of an a document, sets the size of the layer to a specified width and height and export it in a separate file. I finished the script but it really really slow. I read that accessing layers from the DOM is slow and that i should use the ActionManager instead, but I can't find any documentation on how to use the ActionManager.

       

      This is what I have so far:

      Ok so right now I am creating a document with the size I want. Copy the layer I want from the current doc to the new one and save the document. When I see a group I create a folder on the Directory and call the function recursively.

       

      var refDoc;
      var tempDoc;
      
      function main(){
              refDoc = app.activeDocument;
              tempDoc = app.documents.add(500, 500, 72, "tempDoc", NewDocumentMode.RGB, DocumentFill.TRANSPARENT);
              app.activeDocument = refDoc;
              mainFolder = app.activeDocument.path + "/test";
                savetAllLayers(app.activeDocument, mainFolder);
              tempDoc.close (SaveOptions.DONOTSAVECHANGES);
      }
      
      function saveAllLayers(group, folderName){
          var groupFolder = new Folder(folderName)
          for (var i = 0; i < group.layers.length; i++){
                  if (group.layers[i].typename.valueOf() === "LayerSet"){
                      var groupFolder = new Folder(folderName + "/" + group.layers[i].name);
                      if (!groupFolder.exist){
                          groupFolder.create();
                      }
                      saveAllLayers(group.layers[i], groupFolder);
                  }
                  else if (group.layers[i].typename.valueOf() === "ArtLayer"){
                      try {
                          app.activeDocument = refDoc;
                          group.layers[i].copy();
                          app.activeDocument = tempDoc;
                          var targetLayer = tempDoc.artLayers.add();
                          tempDoc.paste();
                          tempDoc.activeLayer.resize(500, 500);
                          var options = new ExportOptionsSaveForWeb();
                          options.fomat = SaveDocumentType.PNG;
                          options.PNG8 = false;
                          options.transparency = true;
                          options.optimized = true;
                          //Export Save for Web in the current folder
                          tempDoc.exportDocument(File(folderName +'/' + group.layers[i].name +'.png'), ExportType.SAVEFORWEB, options);
                          targetLayer.remove();
      
      
                      } catch (e){
                          alert("Error on layer: " + group.layers[i].name + "\n\n\n" + e.message);
                      }
                  }
      
      
          }
          return
      }}
      
      
      

       

       

      How can I use the ActionManager to achieve this? Thank you.

        • 1. Re: Looping through layers is slow.
          c.pfaffenbichler Level 9

          The code in that thread is largely based on stuff Mike and Paul have provided (I may have added mistakes of my own, though) but I can’t locate the threads from which I took it.

          Calculating width height in script

          If you remove the lines

          alert ("width: "+theWidth+", height: "+theHeight+", index: "+m+", name: "+theName);

          selectLayerByIndex(m,false);

          and instead collect the info in an Array for example it should run faster than the corresponding DOM code.

           

          Maybe you should search

          PS-Scripts • Photoshop Scripting Community

          • 2. Re: Looping through layers is slow.
            cbuliarca Level 3

            I don't think that AM will help to much, the time eating operation is the resize and you can't do anything with AM to make it faster,

            the only think in where the AM could help is the going through layers, other than that I don't see how you can make it faster...

             

            Here is a code with AM that will do what your function is doing, I've compared the time for both of them and there is a small improvement with AM in terms of time but it's not that much noticeable.

             

            function BCM_saveAsFolders( pathname ){

              refDoc1 = app.activeDocument;

              var sizes = {w:500, h:500};

                var tempDoc1 = app.documents.add(500,500, 72, "BCM_rrrre_tempDoc1", NewDocumentMode.RGB, DocumentFill.TRANSPARENT); 

              app.activeDocument = refDoc1;

              var ref = new ActionReference();

              ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );

                var aa = 0;

                try{app.activeDocument.backgroundLayer}catch(err){aa = 1};

              var count = executeActionGet(ref).getInteger(charIDToTypeID('Cnt ')) - aa;// the number of layers in the document

              var x, y, r = 0;

              var currentFolder = new Folder(pathname);

              if(!currentFolder.exists){currentFolder.create()};

              for(i = count; i >= 0 ; i--){// the recursive loop

              ref = new ActionReference();

                  ref.putIndex( charIDToTypeID( 'Lyr ' ), i );

                 try{

                  var desc = executeActionGet(ref);

                 }catch(err){ break; }

                 var layerName = desc.getString(charIDToTypeID( 'Nm  ' ));

                    var Id = desc.getInteger(stringIDToTypeID( 'layerID' ));

                    var ls = desc.getEnumerationValue(stringIDToTypeID("layerSection"));

                    ls = typeIDToStringID(ls);

                    var vis = desc.getInteger(stringIDToTypeID( 'visible' ));r

                        if(ls == 'layerSectionContent'){

                        duplicateTotempDoc1umentAndSaveByIDX(i, (currentFolder.fullName +"/"+layerName), sizes, tempDoc1);

                        app.activeDocument = refDoc1;

                        }

                        if(ls == 'layerSectionStart'){// if it's the start of a layer set

                        currentFolder = new Folder(currentFolder.fullName +"/"+layerName);

                        if( !currentFolder.exists ){

                        currentFolder.create();

                        }

                        }

                        if(ls == 'layerSectionEnd'){// if it's the end of a layerSet

                        var cPthStr = currentFolder.fullName.toString();

                        currentFolder = new Folder(cPthStr.substring(0,cPthStr.lastIndexOf("/")));

                        }

              }

                tempDoc1.close (SaveOptions.DONOTSAVECHANGES);

            }

            function duplicateTotempDoc1umentAndSaveByIDX(idx, pathname, size, tempDoc1){

              // =======================================================duplicate layer to a new document

              var idDplc = charIDToTypeID( "Dplc" );

                 var desc57 = new ActionDescriptor();

                 var idnull = charIDToTypeID( "null" );

                     var ref47 = new ActionReference();

                     ref47.putIndex( charIDToTypeID( "Lyr " ), idx );

                 desc57.putReference( idnull, ref47 );

                 var idT = charIDToTypeID( "T   " );

                     var ref48 = new ActionReference();

                     var idDcmn = charIDToTypeID( "Dcmn" );

                     ref48.putName( idDcmn, "BCM_rrrre_tempDoc1" );

                 desc57.putReference( idT, ref48 );

                 var idVrsn = charIDToTypeID( "Vrsn" );

                 desc57.putInteger( idVrsn, 5 );

              executeAction( idDplc, desc57, DialogModes.NO );

             

             

             

              app.activeDocument = tempDoc1;

                tempDoc1.activeLayer.resize(500, 500);

              var options = new ExportOptionsSaveForWeb(); 

              options.format = SaveDocumentType.PNG; 

              options.PNG8 = false; 

              options.transparency = true; 

              options.optimized = true; 

              //Export Save for Web in the current folder 

              tempDoc1.exportDocument(new File(pathname +'.png'), ExportType.SAVEFORWEB, options);

              tempDoc1.activeLayer.remove(); 

            }

            // var startTime = new Date().getTime();

            BCM_saveAsFolders(Folder.desktop +"/test");

            // var endTime = new Date().getTime();

            // alert(endTime - startTime);

             

            • 3. Re: Looping through layers is slow.
              matias.kiviniemi Level 3

              Two things you could try:

              • Instead of Copy/Paste use layer.duplicate()
              • When doing lots of operations, maintaining history can be a bottleneck. Try using
                app.activeDocument.suspendHistory("Executing script, please wait...", "savetAllLayers(app.activeDocument, mainFolder)")
                


              1 person found this helpful
              • 4. Re: Looping through layers is slow.
                danieln1993 Level 1

                Thank you. What exactly does suspenHistory do? and should i call it?

                • 5. Re: Looping through layers is slow.
                  matias.kiviniemi Level 3

                  Sorry, forgot this. suspendHistory tells Photoshop to run a part of script without saving the undo state for each step. Basically everything is one big undo (which is typically what you'd want in any case). Calling it gives one info text (becomes title or something) and the second parameter is a peace of Javascript you want to run like calling one function. Not 100% sure if passing parameters as references/values works.

                  1 person found this helpful