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 21, 2021
  • The 'trackTools' object simply lists the tools for which the script will track the brush settings. You can edit it at your discretion (delete unnecessary ones or write false instead of true)
  • After a tool preset switch, the script determine which tool was activated by this preset and try to load the previously saved brush size and hardness settings for it (all other tool settings will be taken from the preset). Since Photoshop does not have events notifying about changing brush parameters, the settings are saved only at the time of painting on the canvas (if you changed the brush settings and did not make a single stroke, it will not remember them).
  • After restarting Photoshop, the script will remain activated, but the size and hardness of the brushes saved by it will be deleted (that is, it will start working "from scratch"). If you need to remember the previous brush settings after restarting Photoshop, just remove the 'false' argument in the line (or change it to 'true') 

 

if (trackTools[tool.toolName]) app.putCustomOptions(tool.toolName, tool.brush, false)

 

  • There is no need to go to the Script Events Manager - the first time you run the script, it will activate it by itself and make records of the necessary events. When you manually run the script again, it will delete all event notifications.

 

Here's a small example using paintbrush:

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?