Page about The GIMP


This page is dedicated to The GNU Image Manipulation Program (a.k.a. The GIMP). Here you can find some scripts and some discussion on my findings.

Utility scripts

Here's a collection of scripts I've written. Some of them may seem somewhat trivial but they provide very basic functionality which was missing from the GIMP at the time they were written.

numeric-ops.scm - Script that allows performing several operations by numeric entry of the parameters (positions, offsets, etc.)

Here's a list of the operations numeric-ops.scm supports:
 - Place horizontal/vertical guide given its coordinate.
 - Set the layer's position to the given coordinates.
 - Displace the current layer by the given amount.
 - Displace the current selection by the given amount.
 - Select a rectangle given its left, top, right and bottom.
 - Select an ellipse given its bounds.
 - Create an elliptic path given its bounds.
 - Create a rectangular path given its corners.
 - Create a diamond-shaped path given its bounds.
 - NEW: Create a path shaped as a rectangle with round borders given the bounds and the radius.
All of them are placed in the Script-fu/Numeric submenu under the image's context menu. If you have a suggestion for an option not included here, feel free to email me to the address below.

center-ops.scm - This script allows centering of the selection and a layer with respect to either the image or another layer. All of them are installed under the image's context menu. The selection operations are placed in the Script-fu/Selection submenu; the layer operations are placed in the Script-fu/Layer submenu.
If you have a suggestion for an option not included here, feel free to email me to the address below.

show-bounds.scm - Shows the selection boundaries in a popup window (or the image's boundaries in the case that there's no active selection). Registered in "<Image>/Script-fu/Selection/Show Selection Bounds".

change-brush.scm - This script contains two utility functions that change to the previous/next brush. They register into the image's submenu Script-fu/Utils/Keyboard-triggered with the intention that you assign keyboard shortcuts to them. Suggested by lukashi on IRC.

select-layer.scm - This is a simple tool that selects the current layer as opposed to the whole image. It's useful e.g. to use Round Selection on the current layer's extents (that's what I needed it for). This one is registered in the Select submenu of the image's context menu, in an option called "Current Layer".

show-guides.scm - Show the guides that are in an image. Lists them in a window, in pixels, in ascending order.

shared-text.scm - Defines a global text variable that can be shared by various scripts and shown/cleared with the provided functions. See the script for more details.

dot-position-finder.scm - This script lets you create a list of points you click on, in the order you click them. It requires you to press a key after each click. Requires shared-text.scm above. The instructions are in the script itself. You can edit it to suit the list output format you need.

dissolve-frames.scm - Creates a "dissolve" ("cross-fade") effect between two layers, by inserting the intermediate frames between them. The input layers are the selected one and the layer above it (which must exist or the script will complain). Optionally blur the layer fading out or focus in the layer fading in, and optionally do a ping-pong (back and forth).

The effect is similar to Filters > Animation > Blend but this one does not require a third background layer. Note that the insertion is made in place, no new image is created.

Setting the top layer mode to Dissolve (not to be confused with the name of this script) will create a random pixel-by-pixel cross-fade effect instead of opacity-based.

Animated GIF showing various variations of the Dissolve effect (480×1760, 677,756 bytes)

Discussion - Local functions in Script-Fu

For some time I heard that it's not possible in Script-Fu to declare local functions shared by several global functions so as to avoid namespace conflicts. The ancillary functions declared in a file were supposed to be always visible from other files; this has caused trouble sometimes when functions with the same name have been overwritten by another script.

I'm presenting here a method for having true local functions that are callable from global ones while keeping reasonable readability. It is useful especially in the case where a single script publishes several functions that share a common local variable or function. For single functions, you can define the local symbols inside the function and not bother with this, so this method is useful mainly for multiple global functions in the same file sharing locally defined symbols.

The Scheme language provides a mechanism which makes this possible. Even if SIOD, the interpreter in which Script-Fu is was based, lacks some of the standard Scheme features, that turns out not to be a problem in this case. [Update: The new interpreter bundled with Gimp, TinyScheme, of course has no problem with this method, which is expected as it is a more complete Scheme implementation and this method uses standard Scheme features.]

In Scheme, the "set!" construction allows to bind values to variables which are at a scope level outside of the current one, so that these values are visible outside the current scope. On the other hand, the function registering mechanism of Script-Fu requires a function to be declared in the top level of scope for it to be visible to The GIMP. Another important point to note is that in Scheme a variable's value can be a function body; indeed all functions are actually variables whose value is their body. By putting together these three facts, we'll be able to use locally defined functions.

The idea is: the whole file is enclosed within an empty "let" or "let*" except for the initial bindings of the global functions (variables) to dummy values. Then, within the "let" we can use locally defined functions and variables at will, and we'll use "set!" to bind the global functions to their definitions. Here's an example to better understand this idea:

; Declare the global variable which will hold the function.
(define script-fu-do-something 0)

; Enter local scope.
(let ()

  ; Sample local function A.
  (define (local-function-a param1 param2)
          body)

  ; Sample local function B.
  (define (local-function-b param1 param2)
          body)

  ; Assign the body of the global function to the global variable.
  ; This function can use A and B at will, but A and B won't be
  ; visible outside of this 'let' block.
  (set! script-fu-do-something
        (lambda (param1 param2)
                body))

  ; Register the global variable, which is now a function.
  (script-fu-register "script-fu-do-something"
                      ...))

An enhancement to the previous construction is to define a local function and assign it to the global variable using set!, to take advantage of the 'syntactic sugar' that define adds for declaring functions. Here's the idea:

; Declare the global variable which will hold the function.
(define script-fu-do-something 0)

; Enter local scope.
(let ()

  ; Sample local function A.
  (define (local-function-a param1 param2)
          body)

  ; Sample local function B.
  (define (local-function-b param1 param2)
          body)

  ; Define the global function as a local one.
  ; This function can use local functions A and B.
  (define (do-something param1 param2)
          body)

  ; Export the local function to the global variable.
  (set! script-fu-do-something do-something)

  ; Register the global variable, which is now a function.
  (script-fu-register "script-fu-do-something"
                      ...))

It's not important whether the script-fu-register call is inside or outside the let. We're putting it inside just because we're trying to enclose everything possible within the let.

In Scheme it's mandatory that the variables are defined before their value is changed with "set!". SIOD does not require this but it's highly recommended to follow that convention so that if the GIMP's Scheme interpreter ever changes (which is very likely), the script remains valid. That's the reason for the first define statement. [UPDATE: as of GIMP 2.4, that has happened: SIOD has been replaced with TinyScheme which has that restriction on the set! function. The technique described here works with TintScheme just as well.]

There's an example of this construction in the numeric-ops.scm script linked above. Since version 1.3, the functions script-fu-numeric-rect-select and script-fu-numeric-ellipse-select share a common locally defined function, namely map-op.

There have been previous attempts at solving this problem, e.g. Lasm's attempt (as it can be seen later in that thread, that attempt didn't work). The one presented here has been carefully tested and found to work perfectly, even after restarting the GIMP. What is local remains local whilst the global functions can still be called and work flawlessly. The local function names are not visible to the rest of GIMP, just as expected, and declaring another local function with the same name in a different script does not interfere with the code in this function.

Use this address to send me E-mail: parigalo@formauri.es.
NOTE: THIS ADDRESS IS NOT PERMANENT. Always verify this page to send me E-mail because it's likely to change often for spam reasons.

Any kind of suggestions are welcome.

Back


© Copyright 1998,1999,2000,2002,2003,2004,2005 Pedro Gimeno Fortea. All rights reserved.
This is valid XHTML 1.0.