Dynamic Layer Effects

Anyone, especially newbies, asking for help with Photoshop Scripting and Photoshop Automation - as opposed to those contributing to discussion about an aspect of Photoshop Scripting

Moderators: Tom, Kukurykus

sailoraspasia

Dynamic Layer Effects

Post by sailoraspasia »

I am having trouble finding well-documented code on how to dynamically alter layer effects for individual layers. I would like to, for example, take an effect that is already attached to my layer, let's say an outer glow, and then change the color of that glow.

I have seen some snippets of codes in other threads... such as this one http://ps-scripts.com/bb/viewtopic.php?t=2207 that let's me create a drop shadow. But I am having a hell of a time trying to figure out how exactly it works and thus I'm having a hard time recreating it for the other effects such as stroke, color overlay etc. And the "ActionManager" description in the PS Scripting manual definitely leaves something to be desired.

In the xtools kit I also found some functions for making layers visible but it makes all the effects layers visible, but I would like to make each one visible as needed. If that is possible?

Thanks in advance to anyone who tries to help!!
Mike Hale

Dynamic Layer Effects

Post by Mike Hale »

Working with existing layer styles/effects is not easy. For example I can create a script that makes sure the outerGlow effect is on and change the color. But it only works once. When I run it a second time it throws an error and I can't find any reason it should. I even tried deleting the effects before writing the effect desc back to the layer.
Code: Select allfunction main(){
    function getOuterGlowDesc(desc){
        if(desc.hasKey(stringIDToTypeID('outerGlow'))){
            var outerGlowDesc = fxDesc.getObjectValue(stringIDToTypeID('outerGlow'));
        }
        if(undefined != outerGlowDesc) return outerGlowDesc;
    };
    function getFxDesc(){
        var ref = new ActionReference();
        ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
        var desc = executeActionGet(ref);
        if(desc.hasKey(stringIDToTypeID('layerEffects'))){
            var fxDesc = desc.getObjectValue(stringIDToTypeID('layerEffects'));
        }
        if(undefined != fxDesc) return fxDesc;
    };
    function setLayerEffectsDescriptor( effectsDesc ) {
        var desc = new ActionDescriptor();
            var ref = new ActionReference();
            ref.putProperty( charIDToTypeID('Prpr'), charIDToTypeID('Lefx') );
            ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
        desc.putReference( charIDToTypeID('null'), ref );
        desc.putObject( charIDToTypeID('T   '), charIDToTypeID('Lefx'), effectsDesc );
        executeAction( charIDToTypeID('setd'), desc, DialogModes.NO );
    };
    function deleteExistingFx(){
        var desc = new ActionDescriptor();
        var ref = new ActionReference();
        ref.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
        desc.putReference( charIDToTypeID( "null" ), ref );
        executeAction( charIDToTypeID( "dlfx" ), desc, DialogModes.NO );
    };
    var fxDesc = getFxDesc();
    var outerGlowDesc = getOuterGlowDesc(fxDesc);
    outerGlowDesc.putBoolean(charIDToTypeID( "enab" ), true);
    var colorDesc = new ActionDescriptor();
        colorDesc.putDouble( charIDToTypeID( "Rd  " ), 127 );
        colorDesc.putDouble( charIDToTypeID( "Grn " ), 127 );
        colorDesc.putDouble( charIDToTypeID( "Bl  " ), 127 );
    outerGlowDesc.putObject( charIDToTypeID( "Clr " ), charIDToTypeID( "RGBC" ), colorDesc );
    fxDesc.putObject( charIDToTypeID( "OrGl" ), charIDToTypeID( "OrGl" ), outerGlowDesc );
    deleteExistingFx();
    setLayerEffectsDescriptor( fxDesc );
};
main();
larsen67

Dynamic Layer Effects

Post by larsen67 »

Here are a couple of 'layer style' scriptlistener functions that I have used in the past that you may want to experiment with. I have not included any testing for existing styles here the sample is just expecting a document with >3 layers with styles applied… From what little testing I have tried in the past if you use scriptlistener code to apply a layer style then it wipes all previous style data? So you can just apply to overwrite? If you want multi-styles then you either record all at once or you can use temporary layers apply a style then use the recorded copy & paste styles as this appears to 'add to style' rather than overwrite. To me there are still a lot of grey areas with styles but I've not had much dealing with them…
Code: Select all#target photoshop

app.bringToFront();

var docRef = app.activeDocument;

with (docRef) {
   activeLayer = layers[0];
   copyLayerStyle();
   activeLayer = layers[1];
   pasteLayerStyle();
   activeLayer = layers[2];
   clearLayerStyle();
}

function copyLayerStyle() {
   function cTID(s) { return app.charIDToTypeID(s); };
   executeAction( cTID('CpFX'), undefined, DialogModes.NO );
}

function pasteLayerStyle() {
   function cTID(s) { return app.charIDToTypeID(s); };
      var desc01 = new ActionDescriptor();
   executeAction( cTID('PaFX'), desc01, DialogModes.NO );
}

function clearLayerStyle() {
   function cTID(s) { return app.charIDToTypeID(s); };
   function sTID(s) { return app.stringIDToTypeID(s); };
      var desc01 = new ActionDescriptor();
      var ref01 = new ActionReference();
      ref01.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') );
      desc01.putReference( cTID('null'), ref01 );
   executeAction( sTID('disableLayerStyle'), desc01, DialogModes.NO );
}

Code: Select all#target photoshop

app.bringToFront();

var docRef = app.activeDocument;

with (docRef) {
   hideshowAllStyles(false);
   refresh();
   hideshowAllStyles(true);
   refresh();
   activeLayer = layers[0];
   // This makes a new layer from FX
   styleToLayer();
   globalLight(45, 35);
   activeLayer = layers[2];
   hideshowLayerStyle('Hd  ');
   refresh();
   hideshowLayerStyle('Shw ');
   refresh();
}

function hideshowAllStyles(fxbool) {
   function cTID(s) { return app.charIDToTypeID(s); };
      var desc01 = new ActionDescriptor();
      var ref1 = new ActionReference();
      ref1.putProperty( cTID('Prpr'), cTID('lfxv') );
      ref1.putEnumerated( cTID('Dcmn'), cTID('Ordn'), cTID('Trgt') );
      desc01.putReference( cTID('null'), ref1 );
      var desc02 = new ActionDescriptor();
      desc02.putBoolean( cTID('lfxv'), fxbool );
      desc01.putObject( cTID('T   '), cTID('lfxv'), desc02 );
   executeAction( cTID('setd'), desc01, DialogModes.NO );
}

// Be carefull with this!!!
// Can be used to find FX bounds?
function styleToLayer() {
   function cTID(s) { return app.charIDToTypeID(s); };
      var desc01 = new ActionDescriptor();
      var ref13 = new ActionReference();
      ref13.putClass( cTID('Lyr ') );
      desc01.putReference( cTID('null'), ref13 );
      var ref1 = new ActionReference();
      ref1.putProperty( cTID('Prpr'), cTID('Lefx') );
      ref1.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') );
      desc01.putReference( cTID('Usng'), ref1 );
   executeAction( cTID('Mk  '), desc01, DialogModes.NO );
}

function globalLight(ang, alt) {
   // ang -179 to 180
   // alt 0 to 85
   function cTID(s) { return app.charIDToTypeID(s); };
   function sTID(s) { return app.stringIDToTypeID(s); };
      var desc21 = new ActionDescriptor();
      var ref15 = new ActionReference();
      ref15.putProperty( cTID('Prpr'), cTID('gblA') );
      ref15.putEnumerated( cTID('Dcmn'), cTID('Ordn'), cTID('Trgt') );
      desc21.putReference( cTID('null'), ref15 );
      var desc22 = new ActionDescriptor();
      desc22.putUnitDouble( cTID('gagl'), cTID('#Ang'), ang );
      desc22.putUnitDouble( sTID('globalAltitude'), cTID('#Ang'), alt );
      desc21.putObject( cTID('T   '), cTID('gblA'), desc22 );
   executeAction( cTID('setd'), desc21, DialogModes.NO );
}

function hideshowLayerStyle(fxOpt) {
   // 'Hd  ' or 'Shw '
   function cTID(s) { return app.charIDToTypeID(s); };
   function sTID(s) { return app.stringIDToTypeID(s); };
      var desc24 = new ActionDescriptor();
      var list2 = new ActionList();
      var ref16 = new ActionReference();
      ref16.putClass( cTID('Lefx') );
      ref16.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') );
      list2.putReference( ref16 );
      desc24.putList( cTID('null'), list2 );
   executeAction( cTID( fxOpt ), desc24, DialogModes.NO );
}

Mike your code fails for me second time around too? I must learn some of this get stuff…
Mike Hale

Dynamic Layer Effects

Post by Mike Hale »

larsen67 wrote:Mike your code fails for me second time around too? I must learn some of this get stuff…
I think that the way I update the descriptors must corrupt it somehow although I can see any difference( other than the changes ) between the original and the one I'm putting back. I will keep poking around and will let you know if I find a way that really works.

If you want to learn more about using Action Manager this way I recommend that you have a look at Xbytor's getterdemo script. It will create a nice xml output of the descriptors with their keys and data types. I have my own 'poor man's' version that works in ESTK that lets me narrow down on the descriptor, reference or list that I am interested in exploring.
xbytor

Dynamic Layer Effects

Post by xbytor »

One quick note. If you 'get' an ActionDescriptor via executeActionGet, it can cause problems if you use it or any of it's parts in a future call to executeAction. If you are seeing problems when passing a descriptor back, try making a copy instead (using ActionDescriptor.to/fromStream) and pass the copy.

I had to use this technique back with CS or CS2 so it may no longer be appropriate but it may help.
Mike Hale

Dynamic Layer Effects

Post by Mike Hale »

Either I am doing it wrong or to/fromStream only helps a little. My script will now run twice but a third run throws an error.
Code: Select all    function main(){
        function getOuterGlowDesc(desc){
            if(desc.hasKey(stringIDToTypeID('outerGlow'))){
                var outerGlowDesc = fxDesc.getObjectValue(stringIDToTypeID('outerGlow'));
            }
            if(undefined != outerGlowDesc) return outerGlowDesc;
        };
        function getFxDesc(){
            var ref = new ActionReference();
            ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
            var descStream = executeActionGet(ref).toStream();
         var desc =  new ActionDescriptor();
         desc.fromStream(descStream);
            if(desc.hasKey(stringIDToTypeID('layerEffects'))){
                var fxDesc = desc.getObjectValue(stringIDToTypeID('layerEffects'));
            }
            if(undefined != fxDesc) return fxDesc;
        };
        function setLayerEffectsDescriptor( effectsDesc ) {
            var desc = new ActionDescriptor();
                var ref = new ActionReference();
                ref.putProperty( charIDToTypeID('Prpr'), charIDToTypeID('Lefx') );
                ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
            desc.putReference( charIDToTypeID('null'), ref );
            desc.putObject( charIDToTypeID('T   '), charIDToTypeID('Lefx'), effectsDesc );
            executeAction( charIDToTypeID('setd'), desc, DialogModes.NO );
        };
        var fxDesc = getFxDesc();
        var outerGlowDesc = getOuterGlowDesc(fxDesc);
       outerGlowDesc.putBoolean(charIDToTypeID( "enab" ), false);
        var colorDesc = new ActionDescriptor();
            colorDesc.putDouble( charIDToTypeID( "Rd  " ), 240 );
            colorDesc.putDouble( charIDToTypeID( "Grn " ), 240 );
            colorDesc.putDouble( charIDToTypeID( "Bl  " ), 240 );
        outerGlowDesc.putObject( charIDToTypeID( "Clr " ), charIDToTypeID( "RGBC" ), colorDesc );
        fxDesc.putObject( charIDToTypeID( "OrGl" ), charIDToTypeID( "OrGl" ), outerGlowDesc );
        setLayerEffectsDescriptor( fxDesc );
    };
    main();
xbytor

Dynamic Layer Effects

Post by xbytor »

Try duping the outerGlowDesc.
If you pull a Descriptor out you (probably) have to duplicate it before putting it back in.

-X
Mike Hale

Dynamic Layer Effects

Post by Mike Hale »

If I understood what you suggested, it didn't help. I duped both the effects desc and the outerglow. It still only runs twice if you make changes. It seems to run as many times as you like if you don't change any settings.
Code: Select all    function main(){
        function getOuterGlowDesc(desc){
            if(desc.hasKey(stringIDToTypeID('outerGlow'))){
                var outerGlowDescStream = fxDesc.getObjectValue(stringIDToTypeID('outerGlow')).toStream();
            var outerGlowDesc = new ActionDescriptor();
            outerGlowDesc.fromStream(outerGlowDescStream);
            }
            if(undefined != outerGlowDesc) return outerGlowDesc;
        };
        function getFxDesc(){
            var ref = new ActionReference();
            ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
            var descStream = executeActionGet(ref).toStream();
         var desc =  new ActionDescriptor();
         desc.fromStream(descStream);
            if(desc.hasKey(stringIDToTypeID('layerEffects'))){
                var fxDescStream = desc.getObjectValue(stringIDToTypeID('layerEffects')).toStream();
            var fxDesc = new ActionDescriptor();
            fxDesc.fromStream(fxDescStream);
            }
            if(undefined != fxDesc) return fxDesc;
        };
        function setLayerEffectsDescriptor( effectsDesc ) {
            var desc = new ActionDescriptor();
                var ref = new ActionReference();
                ref.putProperty( charIDToTypeID('Prpr'), charIDToTypeID('Lefx') );
                ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
            desc.putReference( charIDToTypeID('null'), ref );
            desc.putObject( charIDToTypeID('T   '), charIDToTypeID('Lefx'), effectsDesc );
            executeAction( charIDToTypeID('setd'), desc, DialogModes.NO );
        };
        var fxDesc = getFxDesc();
        var outerGlowDesc = getOuterGlowDesc(fxDesc);
       outerGlowDesc.putBoolean(charIDToTypeID( "enab" ), false);
        var colorDesc = new ActionDescriptor();
            colorDesc.putDouble( charIDToTypeID( "Rd  " ), 240 );
            colorDesc.putDouble( charIDToTypeID( "Grn " ), 240 );
            colorDesc.putDouble( charIDToTypeID( "Bl  " ), 240 );
        outerGlowDesc.putObject( charIDToTypeID( "Clr " ), charIDToTypeID( "RGBC" ), colorDesc );
        fxDesc.putObject( charIDToTypeID( "OrGl" ), charIDToTypeID( "OrGl" ), outerGlowDesc );
        setLayerEffectsDescriptor( fxDesc );
    };
    main();
xbytor

Dynamic Layer Effects

Post by xbytor »

It's broke. PS, that is, not your script. I tried several other things and nothing worked.
The only thing I haven't tried is replacing the original layer with a dupe and then modifying the layer style.
Mike Hale

Dynamic Layer Effects

Post by Mike Hale »

xbytor wrote:It's broke. PS, that is, not your script.

I was thinking the same thing. Either that or getting the descriptor and creating a new descriptor from scratch using the properties in the original descriptor. And with all the differenct effects and options I don't know that it is worth the effort.