Preview scripts
Stipple Effect has a preview window to give the user a secondary viewport with which to view the project alongside the workspace. By default, the preview window merely displays the project as it is. However, with preview scripts, the user can define an algorithm that transforms the project’s contents for the sake of display in the preview window.
The script is executed every time that the user edits the project. An edit to the project might be a brush stroke or an undo operation or a paste action. However, advancing the frame index, for example, is not considered an edit.
Note:
Preview script transformations merely affect the preview. These transformations are not applied to the project itself. However, the results of preview script transformations can be converted into full-fledged projects.
Contract
How preview scripts must be structured to be accepted by the program
Preview scripts can have a few possible type signatures:
- They must take a single parameter1, 2. This can either be an
image
or an array of imagesimage[]
. - They must return either an
image
or an array of imagesimage[]
. The return type is not correlated with the parameter type.
Thus, the head function of a preview script can take any of the following forms3, 4:
-
(image img -> image) { /* contents here... */ }
-
(image img -> image[]) { /* contents here... */ }
-
(image[] imgs -> image) { /* contents here... */ }
-
(image[] imgs -> image[]) { /* contents here... */ }
Note:
1 - The parameter names img
and imgs
are merely used as examples.
2 - The parameter represented by img
/imgs
can optionally be declared as immutable by prepending final
or ~
to the declaration. For example:
(~ image img -> image) { /* contents here... */ }
3 - Preview scripts do not have to have complex function bodies { ... }
. For example, the following preview script is valid:
(image[] imgs -> image) -> imgs[0]
4 - An image to array of images preview script (signature image img -> image[]
) is only valid for static projects i.e. projects consisting of a single frame.
Example 1
The applications of preview scripts are virtually endless. They can range from simple scripts that transform a project to greyscale, to complex use cases such as this:
Input | Output |
---|---|
Note:
Input is scaled up 4x and output is scaled up 8x and cropped.
This example is achieved with the following script:
(~ image texture -> image[]) {
// constants
~ string BASE_FOLDER = "C:/Users/example/file/path/";
~ string TEX_LOOKUP_FOLDER = BASE_FOLDER + "texture/";
~ string ANIM_LOOKUP_FOLDER = BASE_FOLDER + "anim/";
~ image LOOKUP_ANIM = read_image(ANIM_LOOKUP_FOLDER + "head.png");
~ image LOOKUP_TEX = read_image(TEX_LOOKUP_FOLDER + "head.png");
// uses the lookup animation and lookup texture to map the input texture to its animated equivalent
~ image anim_head = $Graphics.uv_mapping(texture, LOOKUP_TEX, LOOKUP_ANIM);
~ int EXPRESSION_COUNT = 5;
~ int DIRECTIONS = 8;
~ image[] frames = new image[EXPRESSION_COUNT * DIRECTIONS];
for (int i = 0; i < EXPRESSION_COUNT; i++) {
~ image expressionLookup = read_image(ANIM_LOOKUP_FOLDER + "eyebrows_" + i + ".png");
~ image expression = $Graphics.uv_mapping(texture, LOOKUP_TEX, expressionLookup);
for (int j = 0; j < DIRECTIONS; j++) {
~ int fw = expression.w / 8;
~ int fh = expression.h;
~ int x = -fw * j;
~ image frame = new_image_of(fw, fh);
frame.draw(anim_head, x, 0);
frame.draw(expression, x, 0);
frames[(i * DIRECTIONS) + j] = frame;
}
}
return frames;
}
Assets
This script makes use of auxiliary assets to define a UV mapping between a texture and a 2D animation.
These are the assets read from their file paths and stored in the following variables:
File path | Variable | Asset |
---|---|---|
../path/texture/head.png |
LOOKUP_TEX |
|
../path/anim/head.png |
LOOKUP_ANIM |
|
../path/anim/eyebrows_0.png |
expressionLookup (1st loop exec.) |
|
../path/anim/eyebrows_1.png |
expressionLookup (2nd loop exec.) |
|
../path/anim/eyebrows_2.png |
expressionLookup (3rd loop exec.) |
|
../path/anim/eyebrows_3.png |
expressionLookup (4th loop exec.) |
|
../path/anim/eyebrows_4.png |
expressionLookup (5th loop exec.) |
Description
This script takes an input image texture
. The script then reads the image assets listed above.
texture
should have the same dimensions as LOOKUP_TEX
. LOOKUP_ANIM
defines an animation sprite sheet using only colors that are found in LOOKUP_TEX
. Every non-transparent color in LOOKUP_TEX
should occur at only a single pixel.
$Graphics.uv_mapping(...)
uses the texture supplied with the input texture
to produce a textured animation sprite sheet with the auxiliary lookup assets LOOKUP_TEX
and LOOKUP_ANIM
. The way it does this is by:
- scanning every pixel of
LOOKUP_ANIM
- For every non-transparent pixel with color
c_l
at positionx_a
,y_a
inLOOKUP_ANIM
, it looks forx_l
andy_l
- the coordinates of first and ideally only pixel with colorc_l
inLOOKUP_TEX
. - It then samples the color
c_t
of the pixel atx_l
andy_l
intexture
, and paints the pixel at positionx_a
,y_a
of the resultant image with the colorc_t
.
$Graphics.uv_mapping(texture, LOOKUP_TEX, LOOKUP_ANIM)
visualization:
This will produce the following intermediate image as the value of anim_head
:
texture |
anim_head |
---|---|
The outer for
loop has a similar functionality as the code before the loop, but instead of performing a UV mapping for the head, each loop execution performs a UV mapping for a different eyebrow expression.
These are then superimposed on copies of anim_head
, sliced into 8 directions in the inner for
loop, and added to the animation array frames
.
Functions
This script utilizes the following API function:
This script utilizes the following functions from the DeltaScript base language standard library:
read_image(string filepath) -> image
new_image_of(int width, int height) -> image
image::draw(image overlay, int x, int y)
Example 2
Input | Output |
---|---|
This example is achieved with the following script:
(~ image orig -> image) {
~ int w = orig.w; ~ int h = orig.h;
~ bool vert = w > h;
~ (color -> color)[] fs = [ ::iso_r, ::iso_g, ::iso_b ];
~ int CHANNELS = #|fs;
~ image separated = vert
? new_image_of(w, h * CHANNELS)
: new_image_of(w * CHANNELS, h);
for (int i = 0; i < CHANNELS; i++) {
~ image c_img = new_image_of(w, h);
~ (color -> color) f = fs[i];
for (int x = 0; x < w; x++)
for (int y = 0; y < h; y++)
c_img.dot(f.call(orig.pixel(x, y)), x, y);
separated.draw(c_img, vert ? 0 : i * w, vert ? i * h : 0);
}
return separated;
}
iso_r(color c -> color) -> rgba(c.r, 0, 0, c.a)
iso_g(color c -> color) -> rgba(0, c.g, 0, c.a)
iso_b(color c -> color) -> rgba(0, 0, c.b, c.a)
Description
This script arranges three edited copies of the input image orig
next to each other. Each copy has a different RGB color channel isolated: red, green, and blue.
If orig
is wider than it is tall, the copies are stacked vertically. If not, they are stacked side by side.
Functions
This script does not utilize any functions from the scripting API.
However, it makes use of the following functions from the DeltaScript base language standard library:
new_image_of(int width, int height) -> image
rgba(int r, int g, int b, int alpha) -> color
image::dot(color c, int x, int y)
image::draw(image overlay, int x, int y)
image::pixel(int x, int y) -> color
SEE ALSO