Copy link to clipboard
Copied
Davide Barranca just released his new book on UPX scripting. If like his other books, this one should be great and a big help to those, like me, trying to learn UPX.
https://www.ps-scripting.com/uxp-react.html?utm_source=facebook&utm_medium=post&utm_id=uxplaunch
Copy link to clipboard
Copied
Did anybody already read the book? I'm wondering about what kind of information is there in terms of image processing.
I understand it's not the point of the book, but I'm wondering if he explains how image processing scripts will be written?
For example, does he explain how to write a script that... applies an adjustment layer, a trim, and/or a fit image, ... in UXP?
Interested in your thoughts/answers!
Copy link to clipboard
Copied
Request sample content from the author. Most likely this information is not in the book since it is not positioned as a book for beginners (it is assumed that you were previously familiar with extendScript or CEP).
Copy link to clipboard
Copied
I tried getting the sample content via the link on Davide's site but the link didn't work for me. Then contacted Davide ❤️ directly and he pretty much told me the same as what you're saying here.
His youtube series about UXP already explained a lot about batchplay, but it doesn't go into detail too much about what can be done with DOM/actionmanager code, it points us towards Jaroslav's Alchemist plugin which I understand to be similar to the scriptlistener plugin for "classical" extendscript. (Kuddo's to Adobe for the grant!)
I'm familiar with ExtendScript and ScriptUI, but not at all with CEP or UXP.
Where I'm stuck is, how to add any functionality to the plugin. Are there any resources on that?
How to learn how to build up batchplay actions, how to use the alchemist plugin, how to save the plugin's recordings to file (ie: get it to behave like ScriptingListener and generate ScriptingListenerJS.log)
It'd be cool to keep users from needing to learn another GUI and expand on Photoshop, but at some point I'd best explore another route (.Net + lib-vips/net-vips) you know?
Essentially, my question boils down to: Can we do things like this... Select subject > crop > posterize > gaussian blur > find edges > image resize And if that is possible, is it at least somewhat explained in the book?
Wdyt? 🙂
Copy link to clipboard
Copied
I would recommend you to explore the community https://forums.creativeclouddeveloper.com
It discusses issues directly related to UXP. You can find examples of similar functions, or get help from the community.
From myself, I can add that I am slowly rewriting my old scripts to UXP and, as a rule, this does not cause any particular problems. Since I practically did not work with HTML and CSS, the most difficult thing for me is the management of interface elements, and perhaps only this fact will be the reason for purchasing the Davide course
Copy link to clipboard
Copied
That's a good link! Thanks! 😄
Regarding HTML & CSS... I'm in the same boat. I know of it, but it's been since html4 since I've gone anywhere near. Davide's course will definitely be useful for that. That's why I'd love to verify somehow that I'll be able to use the filters I'm mentioning above. Without them, I wouldn't be able to make what I want. With them... Oh boy! 😄
When you're rewriting your scripts... Could you perhaps share, if it's not too much bother, an example of how to apply a filter? Anything is fine really. I can't find examples on the forum / Photoshop UXP API documentation. I think just seeing the general structure of what a dust & scratches or a custom (convolution) would look like in UXP would make my brain "click" you know?
Thanks a bunch!
Copy link to clipboard
Copied
The UXP object model lacks many features, so focus on batchPlay first. Let's try to transform the eventlistener plugin code that is already familiar to us:
var idGsnB = charIDToTypeID( "GsnB" );
var desc228 = new ActionDescriptor();
var idRds = charIDToTypeID( "Rds " );
var idPxl = charIDToTypeID( "#Pxl" );
desc228.putUnitDouble( idRds, idPxl, 34.600000 );
executeAction( idGsnB, desc228, DialogModes.NO );
to make it more convenient to work, we transfer the variables to the function arguments manually or using the cleanSl script:
gaussianBlur(34.6);
function gaussianBlur(Rds) {
var s2t = function (s) {return app.stringIDToTypeID(s); };
var descriptor = new ActionDescriptor();
descriptor.putUnitDouble( s2t( "radius" ), s2t( "pixelsUnit" ), Rds );
executeAction( s2t( "gaussianBlur" ), descriptor, DialogModes.NO );
}
now we rewrite the code for batchPlay:
const batchPlay = require("photoshop").action.batchPlay;
gaussianBlur(34.6)
function gaussianBlur(radius) {
batchPlay(
[
{
_obj: "gaussianBlur",
radius: {
_unit: "pixelsUnit",
_value: radius
},
_options: {
dialogOptions: "dontDisplay"
}
}
], {})
};
as you can see, everything is exactly the same here as in the eventlistener code, just represented as an object.
By analogy, we can write a function for dustAndScratches:
var idDstS = charIDToTypeID( "DstS" );
var desc229 = new ActionDescriptor();
var idRds = charIDToTypeID( "Rds " );
desc229.putInteger( idRds, 30 );
var idThsh = charIDToTypeID( "Thsh" );
desc229.putInteger( idThsh, 29 );
executeAction( idDstS, desc229, DialogModes.NO );
dustAndScratches(30, 29);
function dustAndScratches(Rds, threshold) {
var s2t = function (s) {return app.stringIDToTypeID(s); };
var descriptor = new ActionDescriptor();
descriptor.putInteger( s2t( "radius" ), Rds );
descriptor.putInteger( s2t( "threshold" ), threshold );
executeAction( s2t( "dustAndScratches" ), descriptor, DialogModes.NO );
}
const batchPlay = require("photoshop").action.batchPlay;
dustAndScratches(30, 29)
function dustAndScratches(radius, threshold) {
batchPlay(
[
{
_obj: "dustAndScratches",
radius: radius,
threshold: threshold,
_options: {
dialogOptions: "dontDisplay"
}
}
], {})
};
Having done this manually several times for different functions and different objects, you will quickly learn how to transfer old functions to batchPlay and this will no longer pose a problem.
Similar code can be obtained using the Alchemist plugin, (use Development version from GitHub to see as many events as possible) or using a self-written event listener:
Copy link to clipboard
Copied
Jazz-y that's amazing!Thank you so much for sharing! Looks like if we ever meet, I owe you a bunch of beers. 🙂
The cleanSl script is a very very good find. I can see how manually going through those steps would improve understanding.
So now the trick to me seems to learn about async/sync and promise/await plus cleaning/refactoring scriptlistener code. Davide's got us covered on that for sure!
Thank you so much, I went and bought the book, started reading and hope the people in the foreword are right. 😉 Here's to the comfort zone! Hah.
Copy link to clipboard
Copied
In extendScript you can use the convertJSONdescriptor function to convert any descriptor to JSON format. If you use it to listen for events or to process the result returned by the eventlistener plugin code, you can easily get an object structure for batchPlay, for example:
var idDstS = charIDToTypeID( "DstS" );
var desc229 = new ActionDescriptor();
var idRds = charIDToTypeID( "Rds " );
desc229.putInteger( idRds, 30 );
var idThsh = charIDToTypeID( "Thsh" );
desc229.putInteger( idThsh, 29 );
var result = executeAction( idDstS, desc229, DialogModes.NO );
(d = new ActionDescriptor()).putObject(stringIDToTypeID('object'), stringIDToTypeID('object'), result);
alert(executeAction(stringIDToTypeID('convertJSONdescriptor'), d).getString(stringIDToTypeID('json')))
will return an object
{"_obj":"dustAndScratches","radius":30,"threshold":29}
When porting my old code, I mostly use this way
Copy link to clipboard
Copied
Oh cool, so this basically allows us to take a command out of the scriptlistener.log code, run a dummy script to get the alert with the resulting json and then use it in batchplay. Did I understand it correctly?
I tried it with a custom filter like so
var idCstm = charIDToTypeID("Cstm");
var desc738 = new ActionDescriptor();
var idScl = charIDToTypeID("Scl ");
desc738.putInteger(idScl, 1);
var idOfst = charIDToTypeID("Ofst");
desc738.putInteger(idOfst, 0);
var idMtrx = charIDToTypeID("Mtrx");
var list10 = new ActionList();
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(1);
list10.putInteger(2);
list10.putInteger(1);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(-1);
list10.putInteger(-2);
list10.putInteger(-1);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
list10.putInteger(0);
desc738.putList(idMtrx, list10);
var result = executeAction(idCstm, desc738, DialogModes.NO);
(d = new ActionDescriptor()).putObject(stringIDToTypeID('object'), stringIDToTypeID('object'), result);
$.writeln(
executeAction(stringIDToTypeID('convertJSONdescriptor'), d).getString(stringIDToTypeID('json'))
);
and I got this back:
{"_obj":"object","matrix":[0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,-1,-2,-1,0,0,0,0,0,0],"offset":0,"scale":1}
After some tinkering... Is this now usable in UXP?
const batchPlay = require("photoshop").action.batchPlay;
custom([0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,-1,-2,-1,0,0,0,0,0,0],0,1);
function custom(kernel, offset,scale) {
batchPlay(
[
{
"_obj":"object",
"matrix":kernel,
"offset":offset,
"scale":scale,
_options: {
dialogOptions: "dontDisplay"
}
}
], {})
};
Copy link to clipboard
Copied
you were very close 🙂 change
"_obj":"object",
by
"_obj": "custom",
As @Jarda Bereza wrote above, the result of convertJSONdescriptor may not always be suitable for direct use in batchPlay
@Jef Bracke wrote:
How do you find out that convertJSONdescriptor even exists?
As far as I remember, this feature appeared with one of the CEP versions. Examples of its use can be found in these files:
The greatest value is not the Adobe documentation (unfortunately), but the users, whose experience and knowledge are invaluable. Most of the information can be found in the official Adobe Communities.
Copy link to clipboard
Copied
Thanks for showing what I still needed to correct, could I ask you how you figured that out?
When I got "object" back I already tought it was a bit strange, but given that this is ExtendScript, it's not unusual to only get a part of what you want on the first go. Figured it would work. But usually this is where I end up stuck.
Would @Jarda Bereza's ps-es-to-uxp have been able to help sort that out?
Copy link to clipboard
Copied
Thanks for showing what I still needed to correct, could I ask you how you figured that out?
By @Jef Bracke
Descriptor that Photoshop returns as a result of executeAction do not always contain the classID (I do not know why and how it happens) . If the classID is not specified, then it assigned from the parent object (our .putObject function)
In batchPlay, the _obj property always specifies the name of the event that we are handling. In this case, it is var idCstm = charIDToTypeID ("Cstm") or if you convert it to a string typeIDToStringID (charIDToTypeID ("Cstm")) == "custom"
Copy link to clipboard
Copied
Oh ok I get that.
In short: if you see "_obj":"object" anywhere, you know you have to go look for the stringID and/or the charID.
The way to do that is as you and Jarda described here in the thread.
var idFit = stringIDToTypeID("3caa3434-cb67-11d1-bc43-0060b0a13dc4");
So... In a hypothetical situation where we wouldn't find the stringID but we do have the charID, could we use the charID as well? (It would save me a lot of refactoring time)
Copy link to clipboard
Copied
So... In a hypothetical situation where we wouldn't find the stringID but we do have the charID, could we use the charID as well? (It would save me a lot of refactoring time)
Of course. In Photoshop, some keys only have charID, but no sringID. And vice versa. Add the $ symbol in the batchPlay to denote charID, for example:
_obj: "$LqFy",
It's going to be funny for someone who comes across idFit for the first time.
Yes, because Fit Image.jsx is a script. In ExtendScript, you can assign an eventID to your script for interacting with Photoshop at the event subsystem level using a specially formed resource string. This is very convenient for passing parameters to your script from other sripts or programs (via bridgeTalk), or for recording it into actions (unfortunately, there is no similar functionality in the UXP yet).
Copy link to clipboard
Copied
Jef: This ps-es-to-uxp is made by Adobe. And I think it should be able to deal with it and few other things. And if not we can fork it and change it.
Copy link to clipboard
Copied
Alright, I'll give it a shot tomorrow & keep you posted here.
This is a massive learning opportunity for me. Thank you guys so so much!
Copy link to clipboard
Copied
So in my other reply you see me trying to understand you.
But it does make me wonder... How do you find out that convertJSONdescriptor even exists?
Where can someone look for that kind of information? This forum, documentation, ... ? Any tips are much appreciated, thank you. 🙂
Copy link to clipboard
Copied
You should also check this: https://github.com/adobe-uxp/ps-es-to-uxp this is dedicated tool for conversion. convertJSONdescriptor is very similar to batchPlay but not exactly the same.
Copy link to clipboard
Copied
You might also check my new panel in Alchemist plugin called Sorcerer: https://forums.creativeclouddeveloper.com/t/sorcerer-new-alchemist-panel-to-generate-plugins/3949
Basically it allows you to generate plugin from (Alchemist) generated source within less than an hour. Works best if your script needs very little UI. And also using Occultist panel you can turn action files (.ATN) into plugins very fast 🙂
Copy link to clipboard
Copied
Well... There'll be a bit of GUI work in it for me. That's what sparked my initial interest in Davide's book. That, and UXP/batchplay appear to be the future.
I think that my users would benefit from a plugin (small p, thank you Davide) with a GUI where they select what operations a script should do and then hit a Go button.
Right now I have a growing collection of scripts that users run as batches playing an action that recorded running the script. A bunch of these scripts are near-identical. Just a parameter or two that are set differently. If they can set parameters in a GUI, I can seriously reduce complexity. 25 options would become... maybe 3? 5 max? If I could get that done and distributed, I can add new functionality straight to the plugin. It'll be a lot more noticeable to the user that there's an update when they see a new button appear rather than hoping they see a new line appearing in the File > Scripts menu. 😉
I have to admit that the GUI of Alchemist & Sorcerer are pretty intimidating looking to me.
I've tried working with Alchemist a while back but then I had to focus on another project so I don't have a lot of experience with it yet... It's definitely a useful tool, once you know what you need from it. To me, that was the hard part at that point. The trick that jazz-y showed with the convertJSONdescriptor helps a great deal though. 🙂