The last month or so I have been working on a project that I will talk about shortly. For that I had to do some simple image edits often, and after the third time or so I figured it was better to script them. I had done a little GIMP scripting before so I figured it wouldn’t be hard to write a couple of scripts to do what I wanted (which was very simple to start with). However, every time I had to do something with GIMP scripting I forget the details and I need to check again, so I figured it would be useful to leave some notes for myself and for others who might be interested.
There are two main scripting engines you can use in GIMP: Script-Fu (which uses Scheme, a Lisp dialect), and python-fu (which uses Python). I have used both at some point but this tutorial will use Script-Fu. Unfortunately, making an introduction to Scheme as a language would be too long for this blog post, but you can check the Script-Fu tutorial in the official documentation.
Note that in the example I’m using some function defined in GIMP 2.10
and later, so the example code will not work as-is with older
versions of the software (in particular, you will have to delete the
calls to the functions
gimp-context-set-line-width, and change
gimp-edit-stroke… but if
you do that, the script will lose some functionality).
Types of scripts/uses
There are two typical uses for these scripts: adding extra menu entries so we can call our script from within GIMP itself, and adding functions that we can call from the command-line. The former typically receive images or layers, and the latter typically receive filenames. In both cases we define functions that do what we want (from images/layers or from filenames) and then we either register them in the menu, or leave them as-is so we can call them from the command-line.
Defining a simple function
Create a new file
add-border-to-image.scm inside your GIMP
scripts/ directory. If you don’t know where that is, go to Edit ➝
Preferences ➝ Folders ➝ Scripts. You can use any of the folders in
that list, or even add a new one.
In that file, enter the following text:
(define (add-border-to-image image) (let* ((drawable (car (gimp-image-get-active-layer image)))) ;; The context push allows us to change settings (like the ;; foreground colour) and go back to the previous settings ;; when we pop it. The gimp-image-undo-group-* makes sure ;; that all the operations are considered only one with ;; regards to undo. (gimp-context-push) (gimp-image-undo-group-start image) (gimp-selection-all image) (gimp-selection-shrink image 2) (gimp-context-set-stroke-method STROKE-LINE) (gimp-context-set-line-width 4) (gimp-context-set-foreground "#657487") (gimp-drawable-edit-stroke-selection drawable) (gimp-image-undo-group-end image) (gimp-context-pop)))
This code defines a function called
add-border-to-image which receives
an image and paints a 4-pixel border on it. It also makes sure that
all the operations are considered only one for undo purposes.
Once we have that function, we can add a menu entry for it, or we can prepare it so it’s easy to call from the command-line.
Functions already defined in GIMP
When you write these scripts you will need to check which functions
are already defined in GIMP (like
gimp-drawable-edit-stroke-selection here). You do that by going to
Filters ➝ Script-Fu ➝ Console and then clicking on the “Browse…”
button. You will see a list of functions and a search box you can use
to search for them. Note that by default the search only looks for
functions with those names, so you might want to change that to “by
Adding a menu entry for the function
If we want to be able to paint borders on an image we have open in GIMP, we can add the following code at the end of the file, then either restart GIMP or go to Filters ➝ Script-Fu ➝ Refresh Scripts:
(script-fu-register "add-border-to-image" "<Image>/Edit/Add border" "Paints a 4-pixel border" "Esteban Manchado Velázquez" "Esteban Manchado Velázquez" "2020" "RGB*" SF-IMAGE "Image" 0)
This will add a new menu entry under Edit called “Add border”. The entry will be grayed out if you don’t have any image open. If you do, you will be able to click on the new option and see the newly added border.
Note: for some reason that I haven’t been able to find out, you will need to click anywhere on the image for the border to appear. This is only a problem when using it from the menu.
Calling the function from the command-line
To call the function from the command-line we will have to create a
new function that receives a file path pattern (could be a file path
or something like
images*.png), opens the file(s), calls the
function we defined, and then saves the file(s) somewhere. Add this at
the top of
(define (add-border-to-file file-pattern) (let* ((filelist (cadr (file-glob file-pattern 1)))) (while (not (null? filelist)) (let* ((filename (car filelist)) (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (drawable (car (gimp-image-get-active-layer image))) (output-filename (string-append (car (strbreakup filename ".")) "-focused.png"))) (add-border-to-image image) (gimp-file-save RUN-NONINTERACTIVE image drawable output-filename output-filename) (gimp-image-delete image)) (set! filelist (cdr filelist)))))
This will receive one parameter, namely
file-pattern, interpret it
as a file pattern with possible wildcards (with the
function) and go through every file that matches that pattern. Then,
for every file, it will open it, calculate the output filename for
that file, call
add-border-to-image with the open image, and then
save the result in the calculated output file and close the image.
Calling the function from the command-line
The command-line incantation to call this function is not trivial and
I always forget it. We need to call the
gimp program with the
option (so that the user interface doesn’t load) and the
option (for batch) and pass it some Scheme to call the function we
want (in this case,
add-border-to-file). We also need to call the
gimp-quit so that GIMP quits after the function has
finished. We do so by calling it like this:
gimp -i -b '(add-border-to-file "img*.png")' -b '(gimp-quit 0)'
Automating repetitive tasks with Script-Fu can be extremely useful and save us a bunch of time. We can expose the functionality we create in two ways: via GIMP’s own menus, and via the command-line’s “batch mode”. If one is used to Lisp dialects, using Script-Fu with Scheme is not too difficult; otherwise, Python-Fu can be a better alternative. Both languages have an interactive console inside of GIMP to try things out, plus a documentation browser to see and search for available Script-Fu functions.
If one wants to learn more details, the official documentation has a Script-Fu tutorial.