# GUM v.1.0.0

# 38

# Writing a Script Fu

A Scheme Tutorial for Gimp Users: Author Dov Grobgeld; Copyright 1997, 1998 by Dov Grobgeld License GDPCL.

## Introduction

One of the wonderful features of Gimp is that it all its functionality may be accessed through scripting. The major scripting language for Gimp that has been attached to it today is Scheme. This document will try to give a brief introduction to Scheme, just teaching the essentials in order to write script-fu scripts, without getting into the programming language theory that is so typical of other Scheme references. If you want you can visit Dov's online version of this lesson at http://imagic.weizmann.ac.il/~dov/gimp/scheme-tut.html he has also a lesson about the Perl scripting and Gimp if you want to use Perl in sted of Scheme it's at http://imagic.weizmann.ac.il/~dov/gimp/perl-tut.html.

## Expressions

Scheme is a Lisp variant and all expressions are surrounded by parenthesis. E.g. a list which will calculate the sum of 3 and 4 is written:

    (+ 3 4)

The + sign is the addition function and 3 and 4 are the first and second parameters to this function. Expressions may be nested, so the expression (3+4)*(5/6) would in Scheme be written:

    (* (+ 3 4) (/ 5 6))

White space has no importance so the above expression may as well be written:

    (*
       (+ 3 4)
       (/ 5 6))

## Functions

Besides the four arithmetic functions that are represented through the symbols + - * / there are lots of other functions built into the language. All of them have the form:

    (foo param1 param2 ...)

Additional functions may be defined by the user through the define keyword. E.g. a function that calculates the square value of its single argument may be declared like this:

    (define (square x) (* x x))

and this function may be called through:

    (square 5)

Lisp and its variants make heavy use of lists. Script-fu is no exception and it uses e.g. a list of three elements to write an RGB color. E.g. the color orange would be written:

    '(255 127 0)

The ' sign is necessary to tell Scheme that this is a literal list. If the ' was omitted Scheme would try to look up a function with the name 255 and send it the two parameters 127 and 0, which is obviously not what we want.

To create a variable called orange with the above value, and then set the background color to it, we can write:

    (set! orange '(255 127 0))
    (gimp-set-background-color orange)

### car, cdr and friends (*)

A list in Scheme is always composed of a head and a tail. The head is the first entry in the list, and the tail is the list of the rest of the elements. This means that the list (255 127 63) really means (255 (127 (63 ()))) but Scheme allows the previous form as a short cut. The car function is used to return the head of the list and the cdr (usually pronounced cudder) is used to get the tail of the list.

[The following is a test of the above functions which may interactively be conducted in the Script-Fu console.]

    => (set! color '(255 127 63))
    (255 127 63)
    => (car color)
    255
    => (cdr color)
    (127 63)

To get the blue component of a color it is necessary to apply the cdr function twice and then the car function.

    => (car (cdr (cdr color)))
    63

This is very inconvenient to write. Therefore there are defined abbreviations of the form: cadr, cddr, caddr, etc. that concatenate the operations described above. The previous expression may therefore much more conveniently be written:

    =>(caddr color)
    63

For the Script-Fu writer one of the most important uses of the car function is to access the returned values from the built-in gimp functions. All gimp-functions return a list, and even if the list contains only one element it must be accessed by car. This is e.g. the case for the important functions `gimp-new-image` and `gimp-new-layer` used below.

### Local variables (*)

More experienced Scheme programmers mostly use local variables instead of the global variables described above. This is considered better programming practice and this construct should be recognized in order to be able to read others ScriptFu scripts.

Local variables are declared through the let keyword as in the following example:

    (let* ((a 3)
       (b 4))
       ((* a b)))

Here a and b have a local scope and retain their values only up to the closing parenthesis matching the one before let* .

## The Gimp PDB

All functionality in GIMP is available through the procedural database (PDB). Each procedural database function has a corresponding scheme function mapping. E.g:

    (gimp-image-new 100 150 RGB)

produces a new gimp image of type RGB and size 100x150.

In Gimp, a browser for all the functions in the PDB is included. This browser is available from the main menu through`Xtns>DB BROWSER`. E.g, the DB Browser entry for uni-img, which we will define in the example below looks like this

For the Scheme programmer this information shows that uni-img may be called with three parameters of the types `INT32`, `STRING` and `COLOR`. The different types will be explained below.

## Registering the script with Script-Fu

After a function has been written, it has to be registered with script-fu before it can be used. This is done through the Scheme function script-fu-register. The registering has following purposes:

- Choose the place of the script in the Script-Fu pulldown menus.
- Tell script-fu the type of parameters the script takes and give these parameters default values.
- Register the script as a command in the PDB.

The last point above actually means that a script is from Gimp's viewpoint in no way different from a built-in command or a plugin command. As long as a command is registered in the PDB it can be called by any script or plugin.

The parameters of script-fu-register may be divided into two groups. The first group of seven parameters must always be given. These are:

- The name of the Lisp function.
- The position of the script in the Gimp menus.
- A help string describing the function of the script.
- The script author.
- The script copyright.
- Script date.
- List of valid image types for the script. This only has a meaning on scripts operating on images that already exist.

After these seven parameters have been given, follows a list of the parameters required by the script. Each parameter is given as a group of three items:

- The type of the parameter. Valid types are

    SF-COLOR
       An RGB color.
    SF-TOGGLE
       A true or false value.
    SF-IMAGE
    SF-DRAWABLE
    SF-VALUE
       Any scalar value, string, integer, or floating point.

- A label for script-fu to display when querying for the parameter.
- A default value.

## A commented script

The following script uni.scm receives two parameter from the user, the size of the image, and a color and goes on to produce a uniform image of the requested size and the requested color. Not very useful, but it shows the essential steps in producing a script-fu script. (";" is a mark that the following is a comment)

    ; Define the function of the script and list its parameters
    ; The parameters will be matched with the parameters listed
    ; below in script-fu-register.
    (define (uni-img size color)
       ; Create an img and a layer
       (set! img (car (gimp-image-new size size RGB)))
       (set! layer (car (gimp-layer-new img size size
          RGB "layer 1" 100 NORMAL)))

       ; The following is done for all scripts
       (gimp-image-disable-undo img)
       (gimp-image-add-layer img layer 0)

       ; Here is where the painting starts. We now have an image
       ; and layer and may paint in the layer through the PDB functions
       (gimp-palette-set-background color)
       (gimp-edit-fill img layer)

       ; The following is also done for all script
       (gimp-display-new img)
       (gimp-image-enable-undo img)))

       ; Finally register our script with script-fu.

       (script-fu-register "uni-img"
          "/Xtns/Script-Fu/Tutorials/Uniform image"
          "Creates a uniform image"
          "Dov Grobgeld"
          "Dov Grobgeld"
          "1997"
          ""
          SF-VALUE "size" "100"
          SF-COLOR "color" '(255 127 0)))

To test it copy it to `$HOME/.gimp/scripts/uni.scm` and press Refresh in `Xtns/Script-Fu`. The script Uniform image should now appear in the pulldown menu `Script-Fu>Tutorials>Uniform image`. Selecting this script gives the following popup:

Accepting these default parameters through the OK button gives us the following new image:

It is also possible to access this script through the Script-Fu console by typing the command

    (uni-img 100 '(0 255 127))

### Hanging a script in the image menu

In the uni-img script it was placed under`Xtns>` in the main Gimp window. This is done to create a new image that is independent of earlier images. It is also possible to create a script which works on an already existing image. If in script-fu-register as the second argument is written:

    <Image>/Script-Fu/...

then the script will be available through the Gimp menu that is launched by the right mouse button over an image.

Here is an example script which copies the current layer to a new layer, blurs it and inverts it.

    (define (script-fu-copy-blur                img
                   drawable
                   blur-radius)
        ; Create a new layer
       (set! new-layer (car (gimp-layer-copy drawable 0)))

       ; Give it a name
       (gimp-layer-set-name new-layer "Gauss-blurred")

       ; Add the new layer to the image
       (gimp-image-add-layer img new-layer 0)

       ; Call a plugin to blur the image
       (plug-in-gauss-rle 1 img new-layer blur-radius 1 1)

       ; Invert the new layer
       (gimp-invert img new-layer)

       ; Flush the display
       (gimp-displays-flush)
    )

    (script-fu-register "script-fu-copy-blur"
          "<Image>/Script-Fu/Tutorial/copy-blur"
          "Copy and blur a layer"
          "Dov Grobgeld"
          "Dov Grobgeld"
          "1998"

          "RGB*, GRAY*"
          SF-IMAGE "Image" 0
          SF-DRAWABLE "Layer to blur" 0
          SF-VALUE "Blur strength" "5")

## Painting areas with selections

In uni-img we called the procedure `gimp-edit-fill` to fill the whole image. Looking at the info for `gimp-edit-fill` in the DB browser we find the following:

NAME: | gimp-edit-fill

BLURB: | Fill selected area of drawable

IN: | image | IMAGE | the image
| drawable | DRAWABLE | the drawable to fill

Thus, if we have a selection active when the `gimp-edit-fill` is called, only the selection is painted. There are lots of ways of choosing a selection as can be seen when searching for a ``select'' in the PDB. We will use `gimp-rect-select`, whose entry in the PDB looks as follows:

NAME: | gimp-rect-select

BLURB | Create a rectangular selection over the specified image

IN: | image | IMAGE | the image
| x | FLOAT | x coordinate of upper-left corner of rectangle
| y | FLOAT | y coordinate of upper-left corner of rectangle
| width | FLOAT | the width of the rectangle: width > 0
| height | FLOAT | the height of the rectangle: height > 0
| operation | INT32 | the selection operation: {ADD (0), SUB (1), REPLACE (2), INTERSECT (3)}
| feather | INT32 | feather option for selections (0=FALSE, 1=TRUE)
| feather_radius | FLOAT | radius for feather operation

A simple use of this function which selects the rectangle (x,y,width,height)=(0,25,100,50), paints this region blue, and releases the selection looks as follows:

    (gimp-rect-select img 0 25 100 50 REPLACE 0 0)
    (gimp-palette-set-background '(0 0 255))
    (gimp-edit-fill img layer-one)
    (gimp-selection-none img)

## Loops

The only looping construct that exists in Script-Fu is while

[Note: this constraint is due to the current scheme interpreter SIOD used for Script-Fu. Once the scheme interpreter as planned is changed to Guile, more looping constructs will probably be added.] The while loop looks as follows:

    (while       (condition)
          (statement1)
          (statement2)
          :
          )

Here's an example which draws horizontal lines, 16 pixels high, on an image:

    (set! y 0)
    (while (< y size)
          (gimp-rect-select img 0 y size 16 REPLACE 0 0)
          (gimp-edit-fill img layer-one)
          (set! y (+ y 32)))

## Floating selections

When pasting an image from the clipboard, or when creating text in a a drawable, the result is not put directly in the drawable. Instead it is put into a special temporary layer known as a floating selection. The floating selection may be manipulated in several ways, and finally it is merged into its associated layer, a process known as anchoring.

### Hello World - writing text in an image

When creating text through the `gimp-text` command, the text is always put into a temporary layer. This temporary layer then has to be anchored. Here is an example of creating some text which is pasted into the current drawable:

    (define (script-fu-hello-world img drawable)
       ; Start an undo group. Everything between the start and the end
       ; will be carried out if an undo command is issued.
       (gimp-undo-push-group-start img)

       ; Create the text. See DBbrowser for parameters of gimp-text.
       (set! text-float (car (gimp-text
                img
                drawable
                10 10
                "Hello world"
                0
                1
                30
                1
                "*" "Helvetica" "medium" "r" "*" "*")))

       ; Anchor the selection
       (gimp-floating-sel-anchor text-float)

       ; Complete the undo group
       (gimp-undo-push-group-end img)

       ; Flush output
       (gimp-displays-flush))

    (script-fu-register "script-fu-hello-world"
          "<Image>/Script-Fu/Tutorial/Hello World"
          "Write Hello World in the current image"
          "Dov Grobgeld"
          "Dov Grobgeld"
          "1998"
          "RGB*, GRAY*"
          SF-IMAGE "Image" 0
          SF-DRAWABLE "Layer" 0)

This script shows another feature we haven't mentioned before. The possibility of creating an undo group. All the commands between the commands `gimp-push-undo-group-begin` and `gimp-push-undo-group-end` are undone together if the undo command is issued.

### Copying a selection

To copy a selection, the command `gimp-edit-copy` is used. It places a copy of the selection contents in the cut-buffer. The contents of the cut-buffer may then be pasted into a layer, the same layer or another one, and it is then pasted as a floating layer.

In the following example the selection is copied, pasted into the same layer, offset a fixed distance, finally anchored. Try it by drawing a small blob in the middle of the image, select the blob, and then call this script.

    (define (script-fu-sel-copy img
                                drawable)

       (gimp-undo-push-group-start img)

       (gimp-edit-copy img drawable)
       (set! sel-float (car (gimp-edit-paste img drawable FALSE)))
       (gimp-layer-set-offsets sel-float 100 50)

       ; Anchor the selection
       (gimp-floating-sel-anchor sel-float)

       ; Complete the undo group
       (gimp-undo-push-group-end img)

       ; Flush output
       (gimp-displays-flush))

    (script-fu-register "script-fu-sel-copy"
          "<Image>/Script-Fu/Tutorial/Selection Copy"
          "Copy the selection into the same layer"
          "Dov Grobgeld"
          "Dov Grobgeld"
          "1998"
          "RGB*, GRAY*"
          SF-IMAGE "Image" 0
          SF-DRAWABLE "Layer" 0)

    Generated by fmtoweb (v. 2.9c) written by Peter G. Martin <peterm@zeta.org.au> Last modified: 20 May 1998