2

I'm writing a simple Photoshop script where I need the bounds of the visible portion of the active layer (i.e. the bounds after the mask is applied).

Thankfully, the ArtLayer.bounds property gives you exactly that for any given layer.

However, if the active layer is actually a group (layer set), things get complicated.

First of all, the group's mask effect is completely ignored. And second, the visibility state of the layers is being ignored as well.

So my question is: how do I get the bounds of the visible portion of a layer set (group)?

Or at the very least, how do I get the bounds of the group's mask? If I have that, I can iterate through all the group's visible layers bounds and intersect all of them with the mask bounds to get what I need.

I didn't find any methods or properties in the reference guide that could give me either of those.

Any help would be appreciated! Thanks!

jedivader
  • 828
  • 10
  • 23

1 Answers1

1

Here's a hacky solution, it creates a temp copy of the group, merges it, get its bounds, deletes it and gets back to the group this will deal with invisible layers

var mfDoc = activeDocument;
var mflayer = activeDocument.activeLayer; // currently active layer
var mfnewdLayer = mfDoc.activeLayer.duplicate(); // Dublicates active layer or group (creating a temp layer)
mfDoc.activeLayer = mfnewdLayer; // sets the temp layer as the active layer
mfnewdLayer.merge(); // merges it, this leaves only visible layers

var mfmlayer = activeDocument.activeLayer; //Grab the currently selected layer
var mfheight = mfmlayer.bounds[2]-mfmlayer.bounds[0]; //Grab the H value
var mfwidth = mfmlayer.bounds[3]-mfmlayer.bounds[1]; //Grab the W value

mfmlayer.remove(); // delete the temp layer
mfDoc.activeLayer = mflayer; // gets back to the layer that was active at the begining
mflayer.name = mfheight + " x " + mfwidth // set it's name to the dimintions we now have

now if you want to deal with masks the script gets a little bit longer:

var mfDoc = activeDocument;
var mflayer = activeDocument.activeLayer; // currently active layer
var mfnewdLayer = mfDoc.activeLayer.duplicate(); // Dublicates active layer or group (creating a temp layer)
mfDoc.activeLayer = mfnewdLayer; // sets the temp layer as the active layer
mfnewdLayer.merge(); // merges it, this leaves only visible layers

var mfmlayer = activeDocument.activeLayer; //Grab the currently selected layer

        if (hasVectorMask() == true){ // Only if it has a layer mask
                selectVectorMask(); // Select the vector mask
                rasterizeVectorMask(); // rasterize the vector mask
                applyLayerMask(); // Apply the layer mask
            }
        if (hasLayerMask() == true){ // Only if it has a layer mask
                selectLayerMask(); // Select the layer mask
                applyLayerMask(); // Apply the layer mask
            }
        if (hasFilterMask() == true){ // Only if it has a Smart Filter mask
            }

var mfheight = mfmlayer.bounds[2]-mfmlayer.bounds[0]; //Grab the H value
var mfwidth = mfmlayer.bounds[3]-mfmlayer.bounds[1]; //Grab the W value

mfmlayer.remove(); // delete the temp layer
mfDoc.activeLayer = mflayer; // gets back to the layer that was active at the begining
mflayer.name = mfheight + " x " + mfwidth // set it's name to the dimintions we now have



function hasLayerMask() {
var hasLayerMask = false;
try {
    var ref = new ActionReference();
    var keyUserMaskEnabled = app.charIDToTypeID( 'UsrM' );
    ref.putProperty( app.charIDToTypeID( 'Prpr' ), keyUserMaskEnabled );
    ref.putEnumerated( app.charIDToTypeID( 'Lyr ' ), app.charIDToTypeID( 'Ordn' ), app.charIDToTypeID( 'Trgt' ) );
    var desc = executeActionGet( ref );
    if ( desc.hasKey( keyUserMaskEnabled ) ) {
        hasLayerMask = true;
    }
}catch(e) {
    hasLayerMask = false;
}
return hasLayerMask;
}


function hasVectorMask() {
var hasVectorMask = false;
try {
    var ref = new ActionReference();
    var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
    var keyKind = app.charIDToTypeID( 'Knd ' );
    ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
    var desc = executeActionGet( ref );
    if ( desc.hasKey( keyKind ) ) {
        var kindValue = desc.getEnumerationValue( keyKind );
        if (kindValue == keyVectorMaskEnabled) {
            hasVectorMask = true;
        }
    }
}catch(e) {
    hasVectorMask = false;
}
return hasVectorMask;
}


function hasFilterMask() {
var hasFilterMask = false;
try {
    var ref = new ActionReference();
    var keyFilterMask = app.stringIDToTypeID("hasFilterMask");
    ref.putProperty( app.charIDToTypeID( 'Prpr' ), keyFilterMask);
    ref.putEnumerated( app.charIDToTypeID( 'Lyr ' ), app.charIDToTypeID( 'Ordn' ), app.charIDToTypeID( 'Trgt' ) );
    var desc = executeActionGet( ref );
    if ( desc.hasKey( keyFilterMask ) && desc.getBoolean( keyFilterMask )) {
        hasFilterMask = true;
    }
}catch(e) {
    hasFilterMask = false;
}
return hasFilterMask;
}

function selectLayerMask() {
try{ 
    var id759 = charIDToTypeID( "slct" );
        var desc153 = new ActionDescriptor();
        var id760 = charIDToTypeID( "null" );
            var ref92 = new ActionReference();
            var id761 = charIDToTypeID( "Chnl" );
            var id762 = charIDToTypeID( "Chnl" );
            var id763 = charIDToTypeID( "Msk " );
            ref92.putEnumerated( id761, id762, id763 );
        desc153.putReference( id760, ref92 );
        var id764 = charIDToTypeID( "MkVs" );
        desc153.putBoolean( id764, false );
    executeAction( id759, desc153, DialogModes.NO );
}catch(e) {
    ; // do nothing
}
}

function selectVectorMask() {
try{ 
    var id55 = charIDToTypeID( "slct" );
    var desc15 = new ActionDescriptor();
    var id56 = charIDToTypeID( "null" );
        var ref13 = new ActionReference();
        var id57 = charIDToTypeID( "Path" );
        var id58 = charIDToTypeID( "Path" );
        var id59 = stringIDToTypeID( "vectorMask" );
        ref13.putEnumerated( id57, id58, id59 );
        var id60 = charIDToTypeID( "Lyr " );
        var id61 = charIDToTypeID( "Ordn" );
        var id62 = charIDToTypeID( "Trgt" );
    ref13.putEnumerated( id60, id61, id62 );
desc15.putReference( id56, ref13 );
executeAction( id55, desc15, DialogModes.NO );
}catch(e) {
    ; // do nothing
}
}

function applyLayerMask() {
try{ 
    var id765 = charIDToTypeID( "Dlt " );
        var desc154 = new ActionDescriptor();
        var id766 = charIDToTypeID( "null" );
            var ref93 = new ActionReference();
            var id767 = charIDToTypeID( "Chnl" );
            var id768 = charIDToTypeID( "Ordn" );
            var id769 = charIDToTypeID( "Trgt" );
            ref93.putEnumerated( id767, id768, id769 );
        desc154.putReference( id766, ref93 );
        var id770 = charIDToTypeID( "Aply" );
        desc154.putBoolean( id770, true );
    executeAction( id765, desc154, DialogModes.NO );
}catch(e) {
    ; // do nothing
}
}

Credits: the whole dealing with masks script is from Flatten All Masks.jsx by LeZuse

Maher Fattouh
  • 1,742
  • 16
  • 24
  • thinking of it now, there's a much simpler solution for applying masks and rasterizing. you just create new layer, select it with the temp layer and merge. then get bounds, remove temp layer. – Maher Fattouh Jan 30 '16 at 01:14
  • I ended up doing almost the same thing - I group the layer and merge the group to get the layer bounds. Instead of duplicating the layer, I just work on the layer itself and then I restore the history state that I saved before changing anything. I'm not sure which one is faster - duplicate and delete or restore a history state, but I went for the history state. – jedivader Feb 08 '16 at 19:01
  • I'm guessing your way should be faster. I do believe there must be another way, but there's just too much variables to handle for a simple thing. – Maher Fattouh Feb 08 '16 at 19:14
  • There is another way and I used it in my first version of this script. I took the layer bounds and intersected it with its masks bounds. If the layer was a set (group), I intersected with all its layers/subgroups. This method works much faster, but it deosn't handle properly complex layers with multiple shapes on them. What would ultimately be best is if one could make a selection out of the layer and its masks and intersect those, and get the bounds of the resulting intersected selection - no unnecessary merging, duplicating, grouping etc. – jedivader Feb 08 '16 at 19:39
  • The width and height should be: var mfheight = mfmlayer.bounds[3]-mfmlayer.bounds[1]; //Grab the H value var mfwidth = mfmlayer.bounds[2]-mfmlayer.bounds[0]; //Grab the W value – Marcin Jul 12 '18 at 07:27
  • Does this still work? When running on a selected layer mask group, it sets the name to 0px x 0px. Leading me to believe the bounding box can't be the entire canvas, because it isn't. – Zak44 Mar 28 '19 at 17:35