Wednesday, January 31, 2007

GIMP script-fu tutorial for a throbber generator

I promised, so here's how to write the GIMP plugin for generating a throbber. A throbber is basically an animation like this one:

 

It is a GUI widget that conveys the user the notion that the computer is not hung up, but otherwise busy doing whatever it is told. In the web, you've probably seen it in your browser, on the top right corner, whenever the browser is waiting for pages to load. But then, ajax came, and the browser throbber does not convey all information anymore. If a page is requesting an update for a given element, the browser won't tell the user it is loading anything, so it is up to the page developer to do so. GMail uses a small red progress bar on the top right corner. Others use throbbers, mimicking the current browser behavior, shown on the page element being updated.

GIMP can produce animations, using images, with multiple layers, saved in GIF format. Moreover, it is completely scriptable. Any action you can do interactively in the editor is available via scripting. It is scriptable in lisp and, more recently, in python. I'll use lisp. On uber-geek karma, lisp beats python by a huge margin. The geek adulthood trial is the implementation of your own personal lisp interpreter.

The animation

We'll generate the animation based on a few key frames. Observing a throbber, it is obvious that the key elements are the graphical elements in the circle, the leafs, that appear and disappear. We'll need to have them described. The easiest way to do so, is to have an image with one layer per leaf. Showing the leafs side-by-side, it would be something like this:

 

The animation is then generated by changing the opacity of each leaf for each frame, according to a function that looks like this:

 

For a given animation frame, the opacity curve gives us the opacity of each leaf (green line). For the next animation frame, we move the curve left or right a given offset (red line).

The code

Now, let's write this out. If you wish to follow step by step, create an image with a background layer and four leaf layers. Check yesterday's screencast for a quick howto. Then, open Xtns | Script Fu | Console. You can paste all code there and gimp will interpret it. It's also useful to click "Browse" for reference documentation on gimp functions I use here.

First, a function to set every leaf frame transparent:

(define (throbber-set-transparent img)
(define numlayers (car (gimp-image-get-layers img)))
(define layerlist (cadr (gimp-image-get-layers img)))
(define i 0)
(while (< i (- numlayers 1))
(gimp-layer-set-opacity (aref layerlist i) 0)
(set! i (+ i 1))
)
)

Then, a function to set the transparency for a given leaf and a given offset (0-360) of our transparency function above:

(define (throbber-set-layer-opacity layers layer offset arc)
(define numlayers (car layers))
(define layerlist (cadr layers))
(define g (- (* layer (/ 360 (- numlayers 1))) offset))
(while (or (< g 0) (> g 360))
(if (< g 0) (set! g (+ g 360)))
(if (> g 360) (set! g (- g 360)))
)
(if (< g arc)
(gimp-layer-set-opacity (aref layerlist layer) (/ (* g 100) arc))
)
)

Now, logically, a function to set the transparency for all leaf layers of a given image on a given offset:

(define (throbber-set-opacity img offset arc)
(define layers (gimp-image-get-layers img))
(define lastlayer (- (car (gimp-image-get-layers img)) 2))
(throbber-set-transparent img)
(define layer 0)
(while (<= layer lastlayer)
(throbber-set-layer-opacity layers layer offset arc)
(set! layer (+ layer 1))
)
)

And finally, a function that creates a layer on a new image for every animation step, using the function above:

(define (throbber-new src step arc ms)
(gimp-image-undo-disable src)
(gimp-selection-all src)
(define dst (car (gimp-image-new (car (gimp-image-width src)) (car (gimp-image-height src)) 0)))
(gimp-image-undo-disable dst)


(define i 0)
(while (< i 360)
(throbber-set-opacity src i arc)
(throbber-gimp-copy-visible-to-new-layer src dst
(string-append "Frame "
(number->string (+ 1 (/ i step)))
" ("
(number->string (/ ms (/ 360 step)))
"ms)"))


(set! i (+ i step))
)
(gimp-display-new dst)
)

Two notes here. One, we name each frame using the duration for the animation. This is used by GIMP when saving the image as an animated GIF. Two, I've cheated a bit by hiding gimp complexity for creating a new frame on the new image from the visible frames of the base one. Here's the relevant function:

(define (throbber-gimp-copy-visible-to-new-layer src dst frametitle)
(gimp-edit-copy-visible src)
(define newlayer (car (gimp-layer-new dst
(car (gimp-image-width dst))
(car (gimp-image-height dst))
0
frametitle
100
0)))
(gimp-image-add-layer dst newlayer -1)
(gimp-floating-sel-anchor (car (gimp-edit-paste newlayer 1)))
)

Now, all that is left to do is register the main function in the script-fu menu:

(script-fu-register
"throbber-new"
"/Xtns/Animators/Throbber..."
"Generates a firefox like throbber animation, from an image with one layer per throbber segment"
"Sergio Carvalho "
"Sergio Carvalho"
"30 Jan 2007"
""
SF-IMAGE "Input Image" 0
SF-VALUE "Frame step (degrees)" "10"
SF-VALUE "Visibility arc (degrees)" "90"
SF-VALUE "Total duration (ms)" "2000"
)

It'll be available under Xtns | Animators | Throbber. If you drop the script file under ~/.gimp-/scripts/, it'll be loaded when gimp starts.

Posted by K at 20:59:51 | Permanent Link | Comments (1) |
Comments
1 - Pretty cool stuff :) (Comment this)

Written by: Rui Moura at 2007/02/01 - 03:03:43
Write a comment