Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

How to select transparency with a script?

Explorer ,
Aug 28, 2025 Aug 28, 2025

I'm trying to write a script that will select a contiguous section of fully transparent canvas, but not all transparency. How can I do this?

If it helps, it's fine to assume that there are no pixels with partial transparency, so ignoring anti-aliasing or selection tolerance is okay. It's also okay to pick the contiguous section arbitrarily.

TOPICS
Actions and scripting , Windows
152
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Community Expert ,
Aug 28, 2025 Aug 28, 2025
quote

I'm trying to write a script that will select a contiguous section of fully transparent canvas, but not all transparency. How can I do this?


By @gregd


Is the area to be targeted always in the same X & Y pixel location that a scripted magic wand could be used?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 28, 2025 Aug 28, 2025

No, I need to be able to search the document for the transparent areas.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 29, 2025 Aug 29, 2025

Search or just load all transparency? These are different things.

 

Presuming that the active layer contains transparency:

 

loadLayerTrans();

function loadLayerTrans() {
        var idset = stringIDToTypeID("set");
        var desc323 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref75 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idselection = stringIDToTypeID("selection");
        ref75.putProperty(idchannel, idselection);
        desc323.putReference(idnull, ref75);
        var idto = stringIDToTypeID("to");
        var ref76 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idchannel = stringIDToTypeID("channel");
        var idtransparencyEnum = stringIDToTypeID("transparencyEnum");
        ref76.putEnumerated(idchannel, idchannel, idtransparencyEnum);
        desc323.putReference(idto, ref76);
        executeAction(idset, desc323, DialogModes.NO);
}
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 29, 2025 Aug 29, 2025

I was assuming the search is necessary in order to find a contiguous section of transparency. Otherwise you load all transparency, which I think is what that script is doing for me.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 29, 2025 Aug 29, 2025

Exactly, it is doing just that.

 

 I don't know how you expect this to be automated.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 29, 2025 Aug 29, 2025

I was hoping it would be possible because scripts like David Jensen's Split to Layers are possible.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 29, 2025 Aug 29, 2025

As a picture is worth one thousand words... Perhaps it would help if you posted a sample file, and a mock-up showing which areas should be selected.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 29, 2025 Aug 29, 2025

Sure, no problem! Here's an example. I would like a script that results in a rectangular, circular, or triangular selection:

gregd_0-1756514684616.png

I recently found a clever script that's I'm trying to adapt to work for my needs (like apparently it isn't possible for a script to save a RAW without dialog?) at the very bottom of this thread: https://community.adobe.com/t5/photoshop-ecosystem-discussions/get-index-of-each-pixel/td-p/10022899

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 30, 2025 Aug 30, 2025

@gregd - How would the script know which of the three transparent areas should be selected? You mentioned arbitrary selection, so using a "random" operator?


As far as I know, multiple selections are just a single selection in scripting, at least in ExtendScript, I don't know about in UXP.

 

A selection can be converted to a path, with various degrees of accuracy. Unlike selections, paths are separate items that can be programmatically isolated from the overall collection.

 

So, I can envisage a script that loads all transparency as a selection, converts the selection to paths, which could then be used to individually isolate or select the pixels from the underlying area, based on the "approximate" path coordinates, but using the accuracy and fidelity of the pixels identified by the paths.

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-do-i-seperate-subjects-into-diffe...

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 31, 2025 Aug 31, 2025
LATEST

@gregd - This is what I was suggesting in my previous post:

 

anim-preview.gif

 

/*
Random Selection from Transparent Layer.jsx
Stephen Marsh
v1.0 - 31st August 2025: Initial release
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-select-transparency-with-a-script/td-p/15481286

Based in part on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-do-i-seperate-subjects-into-different-seperate-files-automatically/td-p/14449644
*/

#target photoshop

    (function () {
        try {
            if (!app.documents.length) {
                alert("No documents are open.");
                return;
            }
            // Check for pathItems and abort script if any are found
            if (app.activeDocument.pathItems.length > 0) {
                alert("Script aborted: Path items detected in the document.");
                return; 
            }
            app.activeDocument.suspendHistory("Random Selection from transparent layer", "main()");
        } catch (e) {
            alert("Error: " + e.message + " (Line: " + e.line + ")");
        }
    })();


// Main processing function
function main() {

    var doc = app.activeDocument;

    // Step 1: Duplicate active layer and prepare
    doc.activeLayer.duplicate();
    doc.activeLayer = doc.layers[0];
    doc.activeLayer.name = "tempLayer";

    // Step 2: Load layer transparency and expand selection
    try {
        app.activeDocument.selection.deselect();
    } catch (e) { }
    loadLayerTransparency();
    doc.selection.invert();
    doc.selection.expand(10);

    // Step 3: Convert selection to work path and process
    convertSelectionToWorkPath();
    separateWorkPathIntoPaths();

    // Remove the original work path
    if (doc.pathItems.length > 0) {
        doc.pathItems[0].remove();
    }

    // Step 4: Select random path and create selection
    selectRandomNormalPath(doc);
    convertPathToSelection();
    deselectPaths();

    // Step 5: Fill and cleanup
    doc.selection.invert();
    var fgColor = app.foregroundColor;
    var bgColor = app.backgroundColor;
    resetForegroundBackgroundColors();
    fillWithForeground();
    loadLayerTransparency();
    doc.selection.invert();
    doc.activeLayer.remove();
    removePaths();
    app.foregroundColor = fgColor;
    app.backgroundColor = bgColor;
}


///// Helper functions /////

function loadLayerTransparency() {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
    desc.putReference(stringIDToTypeID("null"), ref);
    var targetRef = new ActionReference();
    targetRef.putEnumerated(
        stringIDToTypeID("channel"),
        stringIDToTypeID("channel"),
        stringIDToTypeID("transparencyEnum")
    );
    desc.putReference(stringIDToTypeID("to"), targetRef);
    executeAction(stringIDToTypeID("set"), desc, DialogModes.NO);
}

function convertSelectionToWorkPath() {
    var desc = new ActionDescriptor();
    var pathRef = new ActionReference();
    pathRef.putClass(charIDToTypeID("Path"));
    desc.putReference(charIDToTypeID("null"), pathRef);
    var selectionRef = new ActionReference();
    selectionRef.putProperty(charIDToTypeID("csel"), charIDToTypeID("fsel"));
    desc.putReference(charIDToTypeID("From"), selectionRef);
    desc.putUnitDouble(charIDToTypeID("Tlrn"), charIDToTypeID("#Pxl"), 2.0); // Set tolerance to 2 pixels
    executeAction(charIDToTypeID("Mk  "), desc, DialogModes.NO);
}

function separateWorkPathIntoPaths() {
    if (!app.documents.length || !app.activeDocument.pathItems.length) {
        return;
    }
    var doc = app.activeDocument;
    var selectedPath = getSelectedPath();
    if (!selectedPath) {
        alert("No path selected");
        return;
    }
    var pathData = extractPathData(doc, selectedPath);
    // Create separate paths from each subpath
    for (var i = 0; i < pathData.length; i++) {
        var subPathArray = [pathData[i]]; // Wrap single subpath in array
        var pathName = selectedPath.name + "_part_" + i;
        createPathFromData(subPathArray, pathName);
    }
}

function extractPathData(doc, path) {
    var originalUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.POINTS;
    try {
        var ref = new ActionReference();
        // Handle different path types
        if (path.kind === PathKind.WORKPATH) {
            ref.putProperty(charIDToTypeID("Path"), charIDToTypeID("WrPt"));
        } else if (path.kind === PathKind.VECTORMASK) {
            ref.putEnumerated(
                charIDToTypeID("Path"),
                charIDToTypeID("Path"),
                stringIDToTypeID("vectorMask")
            );
        } else {
            // Normal path - find its index
            for (var i = 0; i < doc.pathItems.length; i++) {
                if (doc.pathItems[i] === path) {
                    ref.putIndex(charIDToTypeID("Path"), i + 1);
                    break;
                }
            }
        }
        var desc = executeActionGet(ref);
        var pathComponents = desc.getObjectValue(charIDToTypeID("PthC")).getList(stringIDToTypeID('pathComponents'));
        var pathArray = [];
        // Process each subpath
        for (var i = 0; i < pathComponents.count; i++) {
            var component = pathComponents.getObjectValue(i);
            var subpathList = component.getList(stringIDToTypeID("subpathListKey"));
            var operation = component.getEnumerationValue(stringIDToTypeID("shapeOperation"));
            // Process each subpath in the component
            for (var j = 0; j < subpathList.count; j++) {
                var subPath = [];
                var subpathObj = subpathList.getObjectValue(j);
                var points = subpathObj.getList(stringIDToTypeID('points'));
                var isClosed = false;
                try {
                    isClosed = subpathObj.getBoolean(stringIDToTypeID("closedSubpath"));
                } catch (e) { }
                // Extract point data
                for (var k = 0; k < points.count; k++) {
                    var pointObj = points.getObjectValue(k);
                    var anchorObj = pointObj.getObjectValue(stringIDToTypeID("anchor"));
                    var anchor = [
                        anchorObj.getUnitDoubleValue(stringIDToTypeID('horizontal')),
                        anchorObj.getUnitDoubleValue(stringIDToTypeID('vertical'))
                    ];
                    var leftHandle = anchor;
                    var rightHandle = anchor;
                    var isSmooth = false;
                    try {
                        var leftObj = pointObj.getObjectValue(charIDToTypeID("Fwd "));
                        leftHandle = [
                            leftObj.getUnitDoubleValue(stringIDToTypeID('horizontal')),
                            leftObj.getUnitDoubleValue(stringIDToTypeID('vertical'))
                        ];
                    } catch (e) { }
                    try {
                        var rightObj = pointObj.getObjectValue(charIDToTypeID("Bwd "));
                        rightHandle = [
                            rightObj.getUnitDoubleValue(stringIDToTypeID('horizontal')),
                            rightObj.getUnitDoubleValue(stringIDToTypeID('vertical'))
                        ];
                    } catch (e) { }
                    try {
                        isSmooth = pointObj.getBoolean(charIDToTypeID("Smoo"));
                    } catch (e) { }

                    subPath.push([anchor, leftHandle, rightHandle, isSmooth]);
                }
                subPath.push(isClosed);
                subPath.push(operation);
                pathArray.push(subPath);
            }
        }
        return pathArray;
    } finally {
        app.preferences.rulerUnits = originalUnits;
    }
}

function createPathFromData(pathArray, pathName) {
    var originalUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.POINTS;
    try {
        var desc = new ActionDescriptor();
        var pathRef = new ActionReference();
        pathRef.putProperty(charIDToTypeID('Path'), charIDToTypeID('WrPt'));
        desc.putReference(stringIDToTypeID('null'), pathRef);
        var componentList = new ActionList();
        for (var i = 0; i < pathArray.length; i++) {
            var subPath = pathArray[i];
            var componentDesc = new ActionDescriptor();
            var operation = subPath[subPath.length - 1];
            var isClosed = subPath[subPath.length - 2];
            componentDesc.putEnumerated(
                stringIDToTypeID('shapeOperation'),
                stringIDToTypeID('shapeOperation'),
                operation
            );
            var subpathList = new ActionList();
            var subpathDesc = new ActionDescriptor();
            subpathDesc.putBoolean(charIDToTypeID('Clsp'), isClosed);
            var pointsList = new ActionList();
            // Add points (excluding the last two elements which are flags)
            for (var j = 0; j < subPath.length - 2; j++) {
                var point = subPath[j];
                var pointDesc = new ActionDescriptor();
                // Anchor point
                var anchorDesc = new ActionDescriptor();
                anchorDesc.putUnitDouble(charIDToTypeID('Hrzn'), charIDToTypeID('#Rlt'), point[0][0]);
                anchorDesc.putUnitDouble(charIDToTypeID('Vrtc'), charIDToTypeID('#Rlt'), point[0][1]);
                pointDesc.putObject(charIDToTypeID('Anch'), charIDToTypeID('Pnt '), anchorDesc);
                // Forward handle
                var forwardDesc = new ActionDescriptor();
                forwardDesc.putUnitDouble(charIDToTypeID('Hrzn'), charIDToTypeID('#Rlt'), point[1][0]);
                forwardDesc.putUnitDouble(charIDToTypeID('Vrtc'), charIDToTypeID('#Rlt'), point[1][1]);
                pointDesc.putObject(charIDToTypeID('Fwd '), charIDToTypeID('Pnt '), forwardDesc);
                // Backward handle
                var backwardDesc = new ActionDescriptor();
                backwardDesc.putUnitDouble(charIDToTypeID('Hrzn'), charIDToTypeID('#Rlt'), point[2][0]);
                backwardDesc.putUnitDouble(charIDToTypeID('Vrtc'), charIDToTypeID('#Rlt'), point[2][1]);
                pointDesc.putObject(charIDToTypeID('Bwd '), charIDToTypeID('Pnt '), backwardDesc);
                // Smooth flag
                pointDesc.putBoolean(charIDToTypeID('Smoo'), point[3]);
                pointsList.putObject(charIDToTypeID('Pthp'), pointDesc);
            }
            subpathDesc.putList(charIDToTypeID('Pts '), pointsList);
            subpathList.putObject(charIDToTypeID('Sbpl'), subpathDesc);
            componentDesc.putList(charIDToTypeID('SbpL'), subpathList);
            componentList.putObject(charIDToTypeID('PaCm'), componentDesc);
        }
        desc.putList(charIDToTypeID('T   '), componentList);
        executeAction(charIDToTypeID('setd'), desc, DialogModes.NO);
        // Rename the work path
        var doc = app.activeDocument;
        for (var i = 0; i < doc.pathItems.length; i++) {
            if (doc.pathItems[i].kind === PathKind.WORKPATH) {
                doc.pathItems[i].name = pathName;
                return doc.pathItems[i];
            }
        }
    } finally {
        app.preferences.rulerUnits = originalUnits;
    }
    return null;
}

function getSelectedPath() {
    try {
        var ref = new ActionReference();
        ref.putEnumerated(charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
        var desc = executeActionGet(ref);
        var pathName = desc.getString(charIDToTypeID("PthN"));
        var doc = app.activeDocument;
        // Handle work path case
        if (pathName === "Work Path") {
            // Save work path first if it exists
            if (doc.pathItems.length > 0 &&
                doc.pathItems[doc.pathItems.length - 1].kind === PathKind.WORKPATH) {
                saveWorkPath();
            }
            return doc.pathItems[doc.pathItems.length - 1];
        } else {
            return doc.pathItems.getByName(pathName);
        }
    } catch (e) {
        return null;
    }
}

function saveWorkPath() {
    // Select work path
    var selectDesc = new ActionDescriptor();
    var selectRef = new ActionReference();
    selectRef.putProperty(charIDToTypeID("Path"), charIDToTypeID("WrPt"));
    selectDesc.putReference(charIDToTypeID("null"), selectRef);
    executeAction(charIDToTypeID("slct"), selectDesc, DialogModes.NO);
    // Create new path from work path
    var createDesc = new ActionDescriptor();
    var createRef = new ActionReference();
    createRef.putClass(charIDToTypeID("Path"));
    createDesc.putReference(charIDToTypeID("null"), createRef);
    var fromRef = new ActionReference();
    fromRef.putProperty(charIDToTypeID("Path"), charIDToTypeID("WrPt"));
    createDesc.putReference(charIDToTypeID("From"), fromRef);
    createDesc.putString(charIDToTypeID("Nm  "), generateTimestamp());
    executeAction(charIDToTypeID("Mk  "), createDesc, DialogModes.NO);
}

function selectRandomNormalPath(doc) {
    if (!doc || !doc.pathItems || doc.pathItems.length === 0) {
        alert("No path items found.");
        return null;
    }
    var normalPaths = [];
    // Filter for normal paths only
    for (var i = 0; i < doc.pathItems.length; i++) {
        var path = doc.pathItems[i];
        if (path.kind === PathKind.NORMALPATH) {
            normalPaths.push(path);
        }
    }
    if (normalPaths.length === 0) {
        alert("No saved normal paths found.");
        return null;
    }
    // Select random path
    var randomIndex = Math.floor(Math.random() * normalPaths.length);
    var selectedPath = normalPaths[randomIndex];
    // Select the path using Action Manager
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putName(charIDToTypeID("Path"), selectedPath.name);
    desc.putReference(charIDToTypeID("null"), ref);
    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
    return selectedPath;
}

function convertPathToSelection() {
    var desc = new ActionDescriptor();
    var selectionRef = new ActionReference();
    selectionRef.putProperty(charIDToTypeID("Chnl"), charIDToTypeID("fsel"));
    desc.putReference(charIDToTypeID("null"), selectionRef);
    var pathRef = new ActionReference();
    pathRef.putEnumerated(charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
    desc.putReference(charIDToTypeID("T   "), pathRef);
    desc.putInteger(charIDToTypeID("Vrsn"), 1);
    desc.putBoolean(stringIDToTypeID("vectorMaskParams"), true);
    executeAction(charIDToTypeID("setd"), desc, DialogModes.NO);
}

function deselectPaths() {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
    desc.putReference(charIDToTypeID("null"), ref);
    executeAction(charIDToTypeID("Dslc"), desc, DialogModes.NO);
}

function resetForegroundBackgroundColors() {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID("Clr "), charIDToTypeID("Clrs"));
    desc.putReference(charIDToTypeID("null"), ref);
    executeAction(charIDToTypeID("Rset"), desc, DialogModes.NO);
}

function fillWithForeground() {
    var desc = new ActionDescriptor();
    desc.putEnumerated(charIDToTypeID("Usng"), charIDToTypeID("FlCn"), charIDToTypeID("FrgC"));
    desc.putUnitDouble(charIDToTypeID("Opct"), charIDToTypeID("#Prc"), 100.0);
    desc.putEnumerated(charIDToTypeID("Md  "), charIDToTypeID("BlnM"), charIDToTypeID("Nrml"));
    executeAction(charIDToTypeID("Fl  "), desc, DialogModes.NO);
}

function generateTimestamp() {
    var now = new Date();
    var day = now.getDate();
    var month = now.getMonth() + 1; // Month is 0-based
    var year = now.getFullYear();
    var hour = now.getHours();
    var minutes = now.getMinutes();
    var seconds = now.getSeconds();
    return day + "-" + month + "-" + year + "_" + hour + "-" + minutes + "-" + seconds;
}

function removePaths() {
    var doc = activeDocument;
    var pLen = doc.pathItems.length;
    var pArray = [];
    for (var i = 0; i < pLen; i++) {
        pArray.push(doc.pathItems[i].name);
    }
    for (var i = 0; i < pLen; i++) {
        var curPath = doc.pathItems.getByName(pArray[i]);
        curPath.remove();
    }
}

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines