Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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?
Copy link to clipboard
Copied
No, I need to be able to search the document for the transparent areas.
Copy link to clipboard
Copied
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);
}
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
Exactly, it is doing just that.
I don't know how you expect this to be automated.
Copy link to clipboard
Copied
I was hoping it would be possible because scripts like David Jensen's Split to Layers are possible.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
Sure, no problem! Here's an example. I would like a script that results in a rectangular, circular, or triangular selection:
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
Copy link to clipboard
Copied
@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.
Copy link to clipboard
Copied
@gregd - This is what I was suggesting in my previous post:
/*
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();
}
}
Find more inspiration, events, and resources on the new Adobe Community
Explore Now