Skip to main content
Inspiring
February 20, 2021
Answered

Photoshop Tool Preset Parameters via Script

  • February 20, 2021
  • 2 replies
  • 4931 views

Basically, I switch between the stamp tool preset and the mixer brush preset.

Often I end up adjusting the tool size and hardness depending on image characteristics. 

 

Is it possible to script a stamp tool preset that includes all the tool parameters except the stamp tool size and hardness? The idea here is to inherit the last used brush size and hardness.

 

Alternatively, is it possible to edit a toolPrest.tpl file to edit the tool size and hardness parameters to undefined?

 

This topic has been closed for replies.
Correct answer jazz-y

Try this code. Script uses Photoshop events and launches itself when notifications appear, so be sure to save it to disk before running.

On first run, the script will set up event tracking for painting to canvas and switching presets. As you paint on the canvas by any tool from object 'trackTools', Photoshop will call a script to remember the tool's name and brush options. After you switch the preset, the script will receive current tool data and try to find the previously saved brush parameters, if it succeeds, it will update the tool settings and return the previous parameters.

When you restart Photoshop, the brush settings remembered by the script are reset. To disable event tracking, simply run the script again.

 

#target photoshop

var t2s = typeIDToStringID,
    s2t = stringIDToTypeID,
    evt = null,
    tgt = null;

var trackTools = {
    spotHealingBrushTool: true,
    magicStampTool: true,
    paintbrushTool: true,
    pencilTool: true,
    colorReplacementBrushTool: true,
    wetBrushTool: true,
    cloneStampTool: true,
    patternStampTool: true,
    historyBrushTool: true,
    artBrushTool: true,
    eraserTool: true,
    backgroundEraserTool: true,
    blurTool: true,
    sharpenTool: true,
    smudgeTool: true,
    dodgeTool: true,
    burnInTool: true,
    saturationTool: true
};

try { tgt = arguments[0], evt = t2s(arguments[1]) } catch (e) { };

try {
    if (evt) {
        switch (evt) {
            case 'toolModalStateChanged':
                if (t2s(tgt.getEnumerationValue(s2t('kind'))) == 'paint') {
                    if (tool = getCurrentToolOptions())
                        if (trackTools[tool.toolName]) app.putCustomOptions(tool.toolName, tool.brush, false)
                }
                break;
            case 'select':
                if (tool = getCurrentToolOptions()) {
                    try { var tracked = app.getCustomOptions(tool.toolName) } catch (e) { }
                    if (tracked) {
                        tool.brush.putUnitDouble(s2t('diameter'), s2t('pixelsUnit'), tracked.getUnitDoubleValue(s2t('diameter')));
                        tool.brush.putUnitDouble(s2t('hardness'), s2t('percentUnit'), tracked.getUnitDoubleValue(s2t('hardness')));
                        tool.options.putObject(s2t('brush'), s2t('computedBrush'), tool.brush);
                        (r = new ActionReference()).putClass(s2t(tool.toolName));
                        (d = new ActionDescriptor()).putReference(s2t('target'), r);
                        d.putObject(s2t('to'), s2t('target'), tool.options);
                        executeAction(s2t('set'), d, DialogModes.NO);
                    }
                }
                break;
        }
    } else {
        var ntf = new Notifiers,
            status = ntf.checkNotifier();
        if (status) ntf.delNotifier(); else ntf.addNotifier();
        alert('Brush tracking ' + (status ? 'disabled' : 'enabled') + '!\nRun "' + decodeURI(File($.fileName).name) + '" again to ' + (status ? 'enable' : 'disable') + ' it')
    }
} catch (e) { }

function getCurrentToolOptions() {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('tool'));
    r.putEnumerated(s2t('application'), s2t('ordinal'), s2t('targetEnum'));
    var d = executeActionGet(r),
        o = d.getObjectValue(s2t('currentToolOptions'));

    if (o.hasKey(s2t('brush'))) {
        return {
            toolName: t2s(d.getEnumerationType(s2t('tool'))),
            options: o,
            brush: o.getObjectValue(s2t('brush'))
        }
    } else return null

}

function Notifiers() {
    this.addNotifier = function () {
        app.notifiersEnabled = true
        var handlerFile = File($.fileName)
        app.notifiers.add('toolModalStateChanged', handlerFile)
        app.notifiers.add('select', handlerFile, 'toolPreset')
    }

    this.delNotifier = function () {
        var handlerFile = File($.fileName).name,
            len = app.notifiers.length;
        for (var i = 0; i < len; i++) {
            if (app.notifiers[i].eventFile.name == handlerFile) {
                app.notifiers[i].remove(); i--; len--
            }
        }
    }

    this.checkNotifier = function () {
        var handlerFile = File($.fileName).name,
            len = app.notifiers.length;
        for (var i = 0; i < len; i++) {
            if (app.notifiers[i].eventFile.name == handlerFile) return true
        }
        return false
    }
}

 

P.S. I tried to set the brush parameters directly, but this caused unwanted effects, in particular, the scattering option always resetted. Couldn't quickly fix this problem, so the script updates whole tool settings. 

2 replies

jazz-yCorrect answer
Legend
February 21, 2021

Try this code. Script uses Photoshop events and launches itself when notifications appear, so be sure to save it to disk before running.

On first run, the script will set up event tracking for painting to canvas and switching presets. As you paint on the canvas by any tool from object 'trackTools', Photoshop will call a script to remember the tool's name and brush options. After you switch the preset, the script will receive current tool data and try to find the previously saved brush parameters, if it succeeds, it will update the tool settings and return the previous parameters.

When you restart Photoshop, the brush settings remembered by the script are reset. To disable event tracking, simply run the script again.

 

#target photoshop

var t2s = typeIDToStringID,
    s2t = stringIDToTypeID,
    evt = null,
    tgt = null;

var trackTools = {
    spotHealingBrushTool: true,
    magicStampTool: true,
    paintbrushTool: true,
    pencilTool: true,
    colorReplacementBrushTool: true,
    wetBrushTool: true,
    cloneStampTool: true,
    patternStampTool: true,
    historyBrushTool: true,
    artBrushTool: true,
    eraserTool: true,
    backgroundEraserTool: true,
    blurTool: true,
    sharpenTool: true,
    smudgeTool: true,
    dodgeTool: true,
    burnInTool: true,
    saturationTool: true
};

try { tgt = arguments[0], evt = t2s(arguments[1]) } catch (e) { };

try {
    if (evt) {
        switch (evt) {
            case 'toolModalStateChanged':
                if (t2s(tgt.getEnumerationValue(s2t('kind'))) == 'paint') {
                    if (tool = getCurrentToolOptions())
                        if (trackTools[tool.toolName]) app.putCustomOptions(tool.toolName, tool.brush, false)
                }
                break;
            case 'select':
                if (tool = getCurrentToolOptions()) {
                    try { var tracked = app.getCustomOptions(tool.toolName) } catch (e) { }
                    if (tracked) {
                        tool.brush.putUnitDouble(s2t('diameter'), s2t('pixelsUnit'), tracked.getUnitDoubleValue(s2t('diameter')));
                        tool.brush.putUnitDouble(s2t('hardness'), s2t('percentUnit'), tracked.getUnitDoubleValue(s2t('hardness')));
                        tool.options.putObject(s2t('brush'), s2t('computedBrush'), tool.brush);
                        (r = new ActionReference()).putClass(s2t(tool.toolName));
                        (d = new ActionDescriptor()).putReference(s2t('target'), r);
                        d.putObject(s2t('to'), s2t('target'), tool.options);
                        executeAction(s2t('set'), d, DialogModes.NO);
                    }
                }
                break;
        }
    } else {
        var ntf = new Notifiers,
            status = ntf.checkNotifier();
        if (status) ntf.delNotifier(); else ntf.addNotifier();
        alert('Brush tracking ' + (status ? 'disabled' : 'enabled') + '!\nRun "' + decodeURI(File($.fileName).name) + '" again to ' + (status ? 'enable' : 'disable') + ' it')
    }
} catch (e) { }

function getCurrentToolOptions() {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('tool'));
    r.putEnumerated(s2t('application'), s2t('ordinal'), s2t('targetEnum'));
    var d = executeActionGet(r),
        o = d.getObjectValue(s2t('currentToolOptions'));

    if (o.hasKey(s2t('brush'))) {
        return {
            toolName: t2s(d.getEnumerationType(s2t('tool'))),
            options: o,
            brush: o.getObjectValue(s2t('brush'))
        }
    } else return null

}

function Notifiers() {
    this.addNotifier = function () {
        app.notifiersEnabled = true
        var handlerFile = File($.fileName)
        app.notifiers.add('toolModalStateChanged', handlerFile)
        app.notifiers.add('select', handlerFile, 'toolPreset')
    }

    this.delNotifier = function () {
        var handlerFile = File($.fileName).name,
            len = app.notifiers.length;
        for (var i = 0; i < len; i++) {
            if (app.notifiers[i].eventFile.name == handlerFile) {
                app.notifiers[i].remove(); i--; len--
            }
        }
    }

    this.checkNotifier = function () {
        var handlerFile = File($.fileName).name,
            len = app.notifiers.length;
        for (var i = 0; i < len; i++) {
            if (app.notifiers[i].eventFile.name == handlerFile) return true
        }
        return false
    }
}

 

P.S. I tried to set the brush parameters directly, but this caused unwanted effects, in particular, the scattering option always resetted. Couldn't quickly fix this problem, so the script updates whole tool settings. 

Inspiring
February 21, 2021

Thanks for providing this code. I will test it during the week to see how it functions and share what I find.

I like to make sure I understood the script ideas. 

  • When the script is activated Photoshop calls a script to remember the tool name and options from the trackTools object.
  • After a tool preset switch, the script remembers the new tool options and loads the previously saved tool name and options.
  • When Photoshop quits the tool preset return to the default settings

 

To use the Script, the  Script Events Manager can activate the script when the Photoshop application starts to automatically enable it.

 

 

 

 

Legend
February 28, 2021

Yes, I pulled from the source code the preset call. But I am not sure where to place the amendment. In the if statement or the toolPreset function.

 

// select tool preset
try {
if(findCurrentTool() == 'cloneStampTool'){
var presetLabel = 'Clone Stamp Current Layer FS2';
toolPreset(presetLabel);
}
else if (findCurrentTool() == 'wetBrushTool') {
var presetLabel = 'Mixer Brush FS2'
toolPreset(presetLabel);
}
else {}
} catch (error) {
alert(error);
}

 

function toolPreset(presetLabel) {
try {
// =======================================================
var idselect = stringIDToTypeID( "select" );
var desc36 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
var ref15 = new ActionReference();
var idtoolPreset = stringIDToTypeID( "toolPreset" );
ref15.putName( idtoolPreset, presetLabel );
desc36.putReference( idnull, ref15 );
executeAction( idselect, desc36, DialogModes.NO );
} catch (error) {
alert(error)
}
}

function toolPreset(presetLabel) {
    try {
        // =======================================================
        var idselect = stringIDToTypeID("select");
        var desc36 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref15 = new ActionReference();
        var idtoolPreset = stringIDToTypeID("toolPreset");
        ref15.putName(idtoolPreset, presetLabel);
        desc36.putReference(idnull, ref15);
        executeAction(idselect, desc36, DialogModes.NO);


        (d = new ActionDescriptor()).putBoolean(stringIDToTypeID("runFromOtherScirpt"), true)
        executeAction(stringIDToTypeID("6c3cf554-0267-4dcd-a763-4842fc60d204"), d, DialogModes.NO)
    } catch (error) {
        alert(error)
    }
}
Legend
February 21, 2021
Find this text in the tpl-file
DmtrUntF#Pxl and HrdnUntF#Prc
related to your tool.
 
Change the text with hexeditor to DmtRUntF#Pxl and HrdNUntF#Prc as appropriate.
 
Load the tpl-file into photoshop.
 
 
 
Inspiring
February 21, 2021

That sounds good. I have not opened a .tpl file before. And, not sure if I am opening the file correctly.

I try with the mac visual studio code with the hex editor extension and I get a block of numbers and letters that look like the screenshot. I was not able to locate the syntax DmtrUntF#Pxl and HrdNUntF#Prc in this view.

What is the recommended way to open a .tpl file?

 

Inspiring
February 21, 2021

I figure out how to edit the tpl file with a different hex editor.

There are several instances of the hex code DmtrUntF#Pxl and HrdnUntF#Prc in the tpl file.

Apparently belonging to different tools. I did not find the clone stamp tool in the file. The losest is the magicStampTool. How do I identify the clone stamp tool in this tpl file?