JavaScript Transforms
A JavaScript transform is a transform that modifies the images
according to JavaScript
written in a file. You can write the JavaScript yourself, so you can
create your own transforms.
First some cautions:
- JavaScript is not an ideal language for image manipulation - its
main failing in this context is that it is slow. Your dataflow
speeeds will drop dramatically when you have a JavaScript
transform in them. (This slowness does not affect the mpg once it
has been produced - it will run as fast as a normal jpg).
- You cannot manipulate the timeline - no cutting out frames or
looping in sections.
- You can only work on RGB 32 format.
Cautions done, how do we write a JavaScript file for the
transform? I will assume you are familiar with programming, and
comfortable in tackling the prograamming side of things. I
will just describe the interface and how to use it.
A JavaScript file is a plain text file containing instructions in
JavaScript language. For our purposes Gorp requires one function
to be present (and will ignore most others). This is the
important function:
// create a new image from those given.
// frameNumber: integer representing current frame number.
// The parameter images is an array of objects of the following form:
// {
// int width; // width of the frame in pixels
// int height; // height of the frame in pixels
// int[] pixels; // array of ints, one per pixel, representing RGB data (0x00RRGGBB)
// }
// There is one image position in the array per declared input, but any image position
// except the first may be null.
// The new image is returned in the pixel data of images[0]
function getFrame(frameNumber, images) {
if (frameNumber < startFrame) {
// do nothing
} else if (frameNumber > endFrame) {
images[0].pixels = images[1].pixels;
} else {
numFrames = endFrame - startFrame + 1;
thisFrame = frameNumber - startFrame + 1;
endy = (thisFrame * images[0].height) / numFrames;
endx = (thisFrame * images[0].width) / numFrames;
rowOffset = 0;
for (y = 0; y < endy; y++) {
for (x = 0; x < endx; x++) {
images[0].pixels[rowOffset + x] = images[1].pixels[rowOffset + x];
}
rowOffset = rowOffset + images[0].width;
}
}
}
I have left the comments in this piece of code. This function
takes an array of objects from Java, the array has one element for each
input supplied. This implementation uses two inputs, and so must
be hosted by JavaScriptTwoInputs transform. What does this
script do? Well, it implements a rather simple transition between the
two
inputs. If the frame number is less than startFrame then the image from
the first input is returned unchanged. If the frame number is
greater than endFrame then the
image from the second input is returned.
In between we copy pixels from the second frame to the first. I
am sure that you can do better.
There are a couple of impportant things to note here:
- The pixel data in images[0].pixels will be used to create
the output image. So doing nothing will simpply return the
original first image.
- Each pixel is represented by a 4-byte integer. the top byte is
unused, the next contains the red value, the third the green value and
the fourth the blue value. Each colour value can be from 0 -
255. If you add two blue values together and the result is bigger
than 255 then you will affect the green value.
Copy the code above into a text file, lets call it
JavaScript1.js. Now open up Gorp, and add a couple of video
sources, then create a JavaScript transform from the popup. Since
our code wants two inputs, we need to create a 'JavascriptTwoInputs'
transform. When prompted by the edit dialogue, select the script
file 'JavaScript1.ps'.
Now run your flow diagram, and examine the result. Simple,
really. But very slow.
How to use parameters
If you want to allow the user to supply values to your JavaScript
transform you will need to use the parameter interface. Doing so
will also allow your parameters to persist in the file when a diagram
is saved. There are two parts to this:
- Supplying the parameters to Gorp
- Receiving parameter values from Gorp.
Supplying parameters to Gorp
(As a consequence of limitations to the Java/JavaScript interface, this
is a little messier than I would like.)
To supply parameters to Gorp you must implement the getParameters
function and return a string describing the parameters in the correct
format. (The correct format is actually a subset of the
Gorp file format).
- The parameter list must start and end with ''braces": { and
}.
- Between the braces are parameter definitions separated by commas
(',').
- Each parameter is of the format parameterName = parameterValue.
- parameterName is any string you choose, but without spaces.
You should stick to the convention of 'lowerCamelCase'
- parameterValue can be one of the valid formats.
Here is an example:
startFrame = 20;
endFrame = 70;
// return the parameter names and values in an array
function getParameters() {
params = "{startFrame = "+startFrame+", endFrame = "+endFrame+"}";
return params;
}
Here we returnthe value of our two paarameters as numbers.
Receiving Parameters from Gorp
Receiving parameters from Gorp is easy by comparison. Just
implement the setParameter method like this:
startFrame = 20;
endFrame = 70;
function setParameter(parameterName, parameterValue) {
if ("startFrame" == parameterName) {
startFrame = parameterValue;
} else if ("endFrame" == parameterName) {
endFrame = parameterValue;
}
}
When the file is loaded, or when the values have been successfully
edited in a dialogue this method will be called once for each parameter.
There is a problem, however, with letting users supply values: they can
supply the wrong ones! If you look carefully at the method
getFrame, you will see that we divide by endFrame - startFrame what if
the two values are equal? We will attempt to divide by zero and
that will cause the program to fail. What we need to do is check
that the user is supplying reasonable values.
Checking Parameter Values
You can check parameter values the user is entering into the
dialogue before the OK button is pressed, and you can even disaable the
OK button. First we have to change the parameter get and set code
a little, so that the start and end frames travel around together as a
'Range':
startFrame = 20;
endFrame = 70;
// return the parameter names and values in an array
function getParameters() {
params = "{frameRange = Range("+startFrame+", "+endFrame+")}";
return params;
}
function setParameter(parameterName, parameterValue) {
if ("frameRange" == parameterName) {
startFrame = parameterValue.start;
endFrame = parameterValue.end;
}
}
Now we implement the checker method:
function checkParameter(parameterName, parameterValue) {
if ("frameRange" == parameterName) {
start = parameterValue.start;
if (parameterValue.start >= parameterValue.end) {
return "invalid frame range";
}
}
return null;
}
If we think the values are suitable, we return null. If we think
there is an error we return a string describing the error. If we
return an error the OK button in the dialogue is disabled.
I have included the full text of the script file so that you can doownload and try it.