Skip to main content
Tyler229371107i00
Participating Frequently
January 7, 2024
Answered

Script Needed - Convert to Color Fill

  • January 7, 2024
  • 3 replies
  • 1174 views

I'm bouncing from Photoshop to CSP and back to liquefy multiple layers at once since Photoshop can't do that. Certain layer features are lost in the conversion, one being color-fill layers. CSP converts them to a normal layer with any region revealed by the mask filled with a flat color.

I would like a script where I can click on that layer, and it will convert it back into a Color Fill Layer and delete the one CSP made. Can someone help me out? Thanks in advance.

(see attached image)

This topic has been closed for replies.
Correct answer Stephen Marsh

@Tyler229371107i00 

 

The following script will apply the last used liquify mesh to all layers and layer masks in a single pass!

 

/*
Liquify Selected Layers & Masks using Last Mesh.jsx
v1.0, 8th January 2024, Stephen Marsh
*/

#target photoshop

function main() {

    // Save the current dialog display settings
    var savedDisplayDialogs = app.displayDialogs;
    app.displayDialogs = DialogModes.NO;

    // Capture the initial layer visibility and layer selection
    var currentLayersState = getLayersVisiblity();

    // Get the selected layers: courtesy of jazz-y
    var s2t = stringIDToTypeID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var lrs = executeActionGet(r).getList(p),
        sel = new ActionReference();

    // Loop over the selected layers: courtesy of jazz-y
    for (var i = 0; i < lrs.count; i++) {
        sel.putIdentifier(s2t('layer'), p = lrs.getReference(i).getIdentifier(s2t('layerID')));
        (r = new ActionReference()).putIdentifier(s2t('layer'), p);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        executeAction(s2t('select'), d, DialogModes.NO);

        // Raster mask, vector mask not currently supported
        if (hasLayerMask() === true) {
            // Apply the last liquify filter mesh to the layer mask
            selectLayerCompositeChannel("mask");
            applyLastMesh();
        } else {
            // Apply the last liquify filter mesh
            applyLastMesh();
        }
    }
    // Finish the loop

    // Restore the initial layer visibility and selection
    setLayersVisiblity(currentLayersState);

    // Restore the dialogs
    app.displayDialogs = savedDisplayDialogs;

    app.beep();
    alert("Script completed!");
}

// Single history stage undo
activeDocument.suspendHistory("Liquify Selected Layers & Masks using Last Mesh.jsx", "main()");


///// FUNCTIONS /////

function hasLayerMask() {
    // From Adobe's Flatten All Masks.jsx
    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 selectLayerCompositeChannel(chanPara) {
    // "RGB" | "mask"
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var reference = new ActionReference();
    reference.putEnumerated(s2t("channel"), s2t("channel"), s2t(chanPara));
    descriptor.putReference(s2t("null"), reference);
    descriptor.putBoolean(s2t("makeVisible"), false);
    executeAction(s2t("select"), descriptor, DialogModes.NO);
}

function getLayersVisiblity() {
    // by jazz-y
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var targetLayers = executeActionGet(r).getList(p),
        seletion = [],
        visiblity = {};
    for (var i = 0; i < targetLayers.count; i++) seletion.push(targetLayers.getReference(i).getIdentifier());
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var len = executeActionGet(r).getInteger(p);
    for (var i = 1; i <= len; i++) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerSection'));
        r.putIndex(s2t('layer'), i);
        if (t2s(executeActionGet(r).getEnumerationValue(p)) == 'layerSectionEnd') continue;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
        r.putIndex(s2t('layer'), i);
        var id = executeActionGet(r).getInteger(p);
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('visible'));
        r.putIndex(s2t('layer'), i);
        var visible = executeActionGet(r).getBoolean(p);
        visiblity[id] = visible;
    }
    return {
        selection: seletion,
        visiblity: visiblity
    };
}

function setLayersVisiblity(layersStateObject) {
    // by jazz-y
    var s2t = stringIDToTypeID;
    for (var a in layersStateObject.visiblity) {
        makeVisible = layersStateObject.visiblity[a] ? "show" : "hide";
        (r = new ActionReference()).putIdentifier(s2t('layer'), a);
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t(makeVisible), d, DialogModes.NO);
    }
    if (layersStateObject.selection.length) {
        var r = new ActionReference();
        for (var i = 0; i < layersStateObject.selection.length; i++)
            r.putIdentifier(s2t("layer"), layersStateObject.selection[i]);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        d.putBoolean(s2t("makeVisible"), false);
        executeAction(s2t("select"), d, DialogModes.NO);
    } else {
        (r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
    }
}

function applyLastMesh() {
    var idLqFy = charIDToTypeID("LqFy");
    var desc213 = new ActionDescriptor();
    var idLqMD = charIDToTypeID("LqMD");
    desc213.putString(idLqMD, app.preferencesFolder.fsName + "/" + "Liquify Last Mesh.psp");
    executeAction(idLqFy, desc213, DialogModes.NO);
}

 

 

3 replies

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
January 8, 2024

@Tyler229371107i00 

 

The following script will apply the last used liquify mesh to all layers and layer masks in a single pass!

 

/*
Liquify Selected Layers & Masks using Last Mesh.jsx
v1.0, 8th January 2024, Stephen Marsh
*/

#target photoshop

function main() {

    // Save the current dialog display settings
    var savedDisplayDialogs = app.displayDialogs;
    app.displayDialogs = DialogModes.NO;

    // Capture the initial layer visibility and layer selection
    var currentLayersState = getLayersVisiblity();

    // Get the selected layers: courtesy of jazz-y
    var s2t = stringIDToTypeID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var lrs = executeActionGet(r).getList(p),
        sel = new ActionReference();

    // Loop over the selected layers: courtesy of jazz-y
    for (var i = 0; i < lrs.count; i++) {
        sel.putIdentifier(s2t('layer'), p = lrs.getReference(i).getIdentifier(s2t('layerID')));
        (r = new ActionReference()).putIdentifier(s2t('layer'), p);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        executeAction(s2t('select'), d, DialogModes.NO);

        // Raster mask, vector mask not currently supported
        if (hasLayerMask() === true) {
            // Apply the last liquify filter mesh to the layer mask
            selectLayerCompositeChannel("mask");
            applyLastMesh();
        } else {
            // Apply the last liquify filter mesh
            applyLastMesh();
        }
    }
    // Finish the loop

    // Restore the initial layer visibility and selection
    setLayersVisiblity(currentLayersState);

    // Restore the dialogs
    app.displayDialogs = savedDisplayDialogs;

    app.beep();
    alert("Script completed!");
}

// Single history stage undo
activeDocument.suspendHistory("Liquify Selected Layers & Masks using Last Mesh.jsx", "main()");


///// FUNCTIONS /////

function hasLayerMask() {
    // From Adobe's Flatten All Masks.jsx
    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 selectLayerCompositeChannel(chanPara) {
    // "RGB" | "mask"
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var reference = new ActionReference();
    reference.putEnumerated(s2t("channel"), s2t("channel"), s2t(chanPara));
    descriptor.putReference(s2t("null"), reference);
    descriptor.putBoolean(s2t("makeVisible"), false);
    executeAction(s2t("select"), descriptor, DialogModes.NO);
}

function getLayersVisiblity() {
    // by jazz-y
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var targetLayers = executeActionGet(r).getList(p),
        seletion = [],
        visiblity = {};
    for (var i = 0; i < targetLayers.count; i++) seletion.push(targetLayers.getReference(i).getIdentifier());
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var len = executeActionGet(r).getInteger(p);
    for (var i = 1; i <= len; i++) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerSection'));
        r.putIndex(s2t('layer'), i);
        if (t2s(executeActionGet(r).getEnumerationValue(p)) == 'layerSectionEnd') continue;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
        r.putIndex(s2t('layer'), i);
        var id = executeActionGet(r).getInteger(p);
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('visible'));
        r.putIndex(s2t('layer'), i);
        var visible = executeActionGet(r).getBoolean(p);
        visiblity[id] = visible;
    }
    return {
        selection: seletion,
        visiblity: visiblity
    };
}

function setLayersVisiblity(layersStateObject) {
    // by jazz-y
    var s2t = stringIDToTypeID;
    for (var a in layersStateObject.visiblity) {
        makeVisible = layersStateObject.visiblity[a] ? "show" : "hide";
        (r = new ActionReference()).putIdentifier(s2t('layer'), a);
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t(makeVisible), d, DialogModes.NO);
    }
    if (layersStateObject.selection.length) {
        var r = new ActionReference();
        for (var i = 0; i < layersStateObject.selection.length; i++)
            r.putIdentifier(s2t("layer"), layersStateObject.selection[i]);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        d.putBoolean(s2t("makeVisible"), false);
        executeAction(s2t("select"), d, DialogModes.NO);
    } else {
        (r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
    }
}

function applyLastMesh() {
    var idLqFy = charIDToTypeID("LqFy");
    var desc213 = new ActionDescriptor();
    var idLqMD = charIDToTypeID("LqMD");
    desc213.putString(idLqMD, app.preferencesFolder.fsName + "/" + "Liquify Last Mesh.psp");
    executeAction(idLqFy, desc213, DialogModes.NO);
}

 

 

Tyler229371107i00
Participating Frequently
January 8, 2024

Stephen, this is insane. So if I'm understanding this correctly, there are three scripts as options here.

1 - Liquify layer contents for any selected layers

2 - Liquify mask contents for any selected layers

3 - Liquify layer and mask contents for any selected layers

Is that how it should work? =O

Stephen Marsh
Community Expert
Community Expert
January 8, 2024

You got it! As you requested for the 3rd script, if the layer has a mask, only the mask is liquified, not the layer.

Stephen Marsh
Community Expert
Community Expert
January 8, 2024

@Tyler229371107i00 

 

The following script can be run after the first script, it will liquify raster layer masks using the previously used mesh, ignoring the layer data which was previously liquified using the first script.

 

/*
Liquify Selected Layers Masks using Last Mesh.jsx
v1.0, 8th January 2024, Stephen Marsh
*/

#target photoshop

function main() {

    // Save the current dialog display settings
    var savedDisplayDialogs = app.displayDialogs;
    app.displayDialogs = DialogModes.NO;

    // Capture the initial layer visibility and layer selection
    var currentLayersState = getLayersVisiblity();

    // Get the selected layers: courtesy of jazz-y
    var s2t = stringIDToTypeID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var lrs = executeActionGet(r).getList(p),
        sel = new ActionReference();

    // Loop over the selected layers: courtesy of jazz-y
    for (var i = 0; i < lrs.count; i++) {
        sel.putIdentifier(s2t('layer'), p = lrs.getReference(i).getIdentifier(s2t('layerID')));
        (r = new ActionReference()).putIdentifier(s2t('layer'), p);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        executeAction(s2t('select'), d, DialogModes.NO);

        // Raster mask, vector mask not currently supported
        if (hasLayerMask() === true) {
            // Select the mask
            selectLayerCompositeChannel("mask");
            // Apply the last liquify filter mesh
            var idLqFy = charIDToTypeID("LqFy");
            var desc213 = new ActionDescriptor();
            var idLqMD = charIDToTypeID("LqMD");
            desc213.putString(idLqMD, app.preferencesFolder.fsName + "/" + "Liquify Last Mesh.psp");
            executeAction(idLqFy, desc213, DialogModes.NO);
        }
    }
    // Finish the loop

    // Restore the initial layer visibility and selection
    setLayersVisiblity(currentLayersState);

    // Restore the dialogs
    app.displayDialogs = savedDisplayDialogs;

    app.beep();
    alert("Script completed!");
}

// Single history stage undo
activeDocument.suspendHistory("Liquify Selected Layers Masks using Last Mesh.jsx", "main()");


///// FUNCTIONS /////

function hasLayerMask() {
    // From Adobe's Flatten All Masks.jsx
    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 selectLayerCompositeChannel(chanPara) {
    // "RGB" | "mask"
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var reference = new ActionReference();
    reference.putEnumerated(s2t("channel"), s2t("channel"), s2t(chanPara));
    descriptor.putReference(s2t("null"), reference);
    descriptor.putBoolean(s2t("makeVisible"), false);
    executeAction(s2t("select"), descriptor, DialogModes.NO);
}

function getLayersVisiblity() {
    // by jazz-y
    var s2t = stringIDToTypeID,
        t2s = typeIDToStringID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var targetLayers = executeActionGet(r).getList(p),
        seletion = [],
        visiblity = {};
    for (var i = 0; i < targetLayers.count; i++) seletion.push(targetLayers.getReference(i).getIdentifier());
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
    r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
    var len = executeActionGet(r).getInteger(p);
    for (var i = 1; i <= len; i++) {
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerSection'));
        r.putIndex(s2t('layer'), i);
        if (t2s(executeActionGet(r).getEnumerationValue(p)) == 'layerSectionEnd') continue;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
        r.putIndex(s2t('layer'), i);
        var id = executeActionGet(r).getInteger(p);
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('visible'));
        r.putIndex(s2t('layer'), i);
        var visible = executeActionGet(r).getBoolean(p);
        visiblity[id] = visible;
    }
    return {
        selection: seletion,
        visiblity: visiblity
    };
}

function setLayersVisiblity(layersStateObject) {
    // by jazz-y
    var s2t = stringIDToTypeID;
    for (var a in layersStateObject.visiblity) {
        makeVisible = layersStateObject.visiblity[a] ? "show" : "hide";
        (r = new ActionReference()).putIdentifier(s2t('layer'), a);
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t(makeVisible), d, DialogModes.NO);
    }
    if (layersStateObject.selection.length) {
        var r = new ActionReference();
        for (var i = 0; i < layersStateObject.selection.length; i++)
            r.putIdentifier(s2t("layer"), layersStateObject.selection[i]);
        (d = new ActionDescriptor()).putReference(s2t("target"), r);
        d.putBoolean(s2t("makeVisible"), false);
        executeAction(s2t("select"), d, DialogModes.NO);
    } else {
        (r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
        (d = new ActionDescriptor()).putReference(s2t('target'), r);
        executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
    }
}

 

 

The final step will be to combine both scripts into one.

Tyler229371107i00
Participating Frequently
January 7, 2024

Alternatively, if someone can write a script to take a liquified edit and apply it to a selection of multiple layers, that would be an even better solution so I don't have to use CSP. Still, I've contacted a scripter before, and they said Adobe doesn't let you do that. I'm an artist, so I have no idea if that is true.

Stephen Marsh
Community Expert
Community Expert
January 8, 2024

You can use the last liquify mesh settings... Or save and load mesh settings from a .msh file. If working in the same session you can just reapply the previous Liquify to new layers which will automatically use the last mesh.

 

Tyler229371107i00
Participating Frequently
January 8, 2024
quote

Wow, Stephen you must be a wizard! It works! These are just a couple of other things that are not critical, but I'm curious about your expertise if you have any thoughts on these. 

- For some of the layers I want to liquify, I'm looking to liquefy the mask on the layer instead of the layer contents. It doesn't seem like there is a way to select those to run the action on them without going one by one

 

Thanks for the kind words, most of the script was borrowed and repurposed code from jazz-y!

 

Thinking out loud, it should be simple enough to create a variation of the previous script that would select the layer mask and run liquify, rather than running on the layer. It would be assumed that the masks are raster, not vector.

 

quote

- I would always want to use the same exact liquified mesh for all the layers. Does it need to 'load mesh' for each layer? That seems to be the slowest aspect of the script at the moment.

Either way, what you've created was supposedly impossible, and I'm really appreciative of your help with this!


By @Tyler229371107i00

 

If I'm understanding you correctly, then yes, it uses the automatically saved last used mesh preset.

 

There is no magic here, the script is simply looping over all selected layers and running the same liquify mesh command again on each layer. The looping takes time, the liquify command takes time for each new layer.

 


Sure, that makes sense regarding the loading of the mesh each time. I was just hoping there COULD be some magic time saving trick there. 

Your idea on having an alternate script that would select the mask of the layer to liquify instead of the main layer contents seems like an excellent way to approach it. It wouldn't be much trouble at all to run two scripts on the file. Once for all the normal layers and once for all the layers with the masks. All the masks I work with are raster, not vector.