How to get selection using JSScripting? - RealWorld forums

Log-in or register.

How to get selection using JSScripting?

jediYellow
on October 21st 2022

I have a script that I mentioned before that recolors pixels monochromically. Within an UI mockup I use it to change the states of various elements, icons and text mostly. The script currently works on everything on a layer, and as the mockups are sizeable, the script is too slow. So I have to open the file with the original icon in a new RW window, run the script there and then replace it in the mockup. It would be so much smoother if you gave me the method to get the selection.

Here is the script for your users (it's simple):

"Configuration"

Configuration.AddEditBox("red", "Red Value", "0 - 255",
Configuration.GetValueOrDefault("red", 0));
Configuration.AddEditBox("green", "Green Value", "0 - 255",
Configuration.GetValueOrDefault("green", 0));
Configuration.AddEditBox("blue", "Blue Value", "0 - 255",
Configuration.GetValueOrDefault("blue", 0));

"Execution"

// place your custom JavaScript code here
// the code below changes the color of every pixel
// while leaving alpha
var red = Configuration.GetValue("red");
if (red < 0) red = 0;
if (red > 255) red = 255;
var green = Configuration.GetValue("green");
if (green < 0) green = 0;
if (green > 255) green = 255;
var blue = Configuration.GetValue("blue");
if (blue < 0) blue = 0;
if (blue > 255) blue = 255;
var image = Document.RasterImage;
var sizeX = image.sizeX;
var sizeY = image.sizeY;
for (x=0; x<sizeX; x++)
	{
	for (y=0; y<sizeY; y++)
		{
		if (image.GetPixelAlpha(x, y, 0, 0)!= 0)
			{
			image.SetPixelRed(x, y, 0, 0, red);
			image.SetPixelGreen(x, y, 0, 0, green);
			image.SetPixelBlue(x, y, 0, 0, blue);
			}
		}
	}
Vlasta
on October 21st 2022

You currently can access the selection from the script using the Blender object via the CanvasFromMask method, you should use "SELECTION" as the maskID parameter. Though, the script operation in the toolbar should be preconfigured to actually take the floating selection (not the classic selection) into account. The floating selection should be sent into the script as the "Document" so it should work automatically.

Also, looking at the script, I think you could get a similar effect by using the retouch tool in a Colorize, Add color or Multiply color mode. There is no direct replace color mode, but maybe that is not an obstacle.

Regarding speed, operation like this could be achieved using the Blender object's Compose method by using the OpMapChannels, if my memory is right.

jediYellow
on October 21st 2022

The documentation is ,er ...lacking. I would guess the Blender.CanvasFromMask(object context is my Document.RasterImage, but I really don't get the use of a Context object. An enumeration of Context objects would help :-) though all I can find are the methods and properties assignable to a context.
I did assume at some point that my selection would be vis a vis the document in the script, but that's not being followed.
Also, I saw some of your recent YouTube vids. The new layers panel is exciting. The composite editing should reduce a lot of back and forth effort. You've condensed the concepts into a neat form!

Anyway, in my toolbar configuration, under Configuration Root is Commands, which I have 2 items. The first item is Document - Extract Document Part under which I've placed a Command List with 4 similar items, Document Operations > JavaScript. This script is one of the four.
There is also the second Commands item : a Document Operation > JavaScript (set aside without the Document - Extract Document Part because the script fails if I do).

I've tried to run the above script without the Document - ExtractDocumentPart, but it fails to recognize the assignment of the Document.RasterImage object.

Vlasta
on October 22nd 2022

The context parameter should be the global Context object. So using just

var mask = Blender.CanvasFromMask(Context, "IMAGEMASK");

should do. (And I actually made a mistake in the previous post, the correct default id of the image mask is "IMAGEMASK" and not "SELECTION".)

Each window in RW app holds a document and several states. These states are accessible via the Context object and can have arbitrary identifiers. When someone (script, normal command or a subwindow) sets one of the states, all subwindows are notified. The states usually hold non-persistent information that needs to be remembered while the document is open, for example which layer is selected, what tool is active, current zoom level or the image mask. The identifiers are configurable in the window layout and if two subwindows are to be synchronized they just need to use the same id. Hope this makes it clearer.

If you want to work with the floating selection (the one that is produced by the Transform tool) and not the mask, you should have "Raster Image - Work with Selection" operation being one of the predecessors of the JavaScript operation. It will check the window and if a floating selection exists, it will feed it to its child instead of the document it received from its parent.

The "Document - ExtractDocumentPart" takes a part of the document it receives and sends that part to its child, usually using given state to identify which part to send. So, for example if you are working with a layered image and you want your script to to only work witrh the currently selected layer, it is simpler to use this helper and have it send just the current layer into the JavaScript operation and then you can access it via the RasterImage helper (if the current layer is a raster layer).

jediYellow
on October 24th 2022

Thank you for explaining a bit more about these elements and the organization of the scripting API. It's easy to see that the scripting is built on top of the app, giving the user direct access to the power of RW Paint.

Gooool! Using "Raster Image - Work with Selection" >> "Document Operation" >> "JavaScript" seems to be the right command chain to get it working. And then, as you mentioned, I didn't even have to remove "Document.RasterImage" as my object. Neat.

Thanks :-)

jediYellow
on October 24th 2022

Offsides :-(
Raster Image - Work with Selection doesn't work with the layer if there is no selection (throws a null object exception). However by setting it up as follows Document - Extract Document Part >> Raster Image - Work with Selection >> Document Op >> JavaScript and ticking the Entire Image checkbox even if there is no selection it the script will be applied to the topmost layer.
Maybe if I use the Raster Image - Work with Selection and within the script check if the Document object is null, then get the current layer using LayeredImage.GetIndicesFromState(Context, string state) [just guessing], I can run the script on the layer as Document. Or rather is the current layer accessible through the LayeredImage properties/methods? If the returned array had more than one indices I could return without doing anything.

Vlasta
on October 24th 2022

Document - Extract Document Part has an From current selection option and when you choose it, you need to also specify the ID of the selection, which should be most likely LAYER. Then it will forward the layer currently selected in the user interface to the internal operation.

This does similar things as you can do manually with LayeredImage.GetIndicesFromState(Context, string state) The whole sytem is actually a tiny bit more complicated, but that should not matter here.

I understand that it seems superfluous to be necessary to specify the ID (why can't it just see the obvious selection?), but the sub-doc extraction is a generic part of the program and it can be used in various contexts. For example when you edit an animation, it can be used to access individual frames, when editing an icon or cursor in the other editors, it provides access to the various sizes that are present. Also, the layer does not need to be a raster image and can have further substructure. So, for the extractor, the selection is not obvious.

(if Document is null, you won't be able to access Document.LayeredImage)

jediYellow
on October 24th 2022

Alright, Vlasta, that works. :-) :)

Document - Extract Document Part (From current selection = LAYER) >> Raster Image - Work with Selection (Entire image, selected) >> Document Operation >> JavaScript. The script now works with both floating selection tool otherwise layer.

Thank you for taking the time to help me and teach me how to get this done. It's a convenience.

It's interesting that if a script only works with a selection, the icon disappears from the toolbar when no selection exists. And vice versa for scripts that only work with whole layers or image.

(if Document is null, you won't be able to access Document.LayeredImage)
gotcha

Page views: 443       Posts: 8      
What about ICL files?
Select background
I wish there were...