tutoriel pour faire des Scripts-fu

Ce tutoriel est la traduction du Script-Fu Tutorial de grumbel@gmx.de.
La mise en forme de ce tutoriel respecte exactement celle du tutoriel d'origine.

Préface

le document suivant est écrit pour Gimp 1.1.24 Les actions décrites diffèrent légèrement de la version 1.0 ou de la version 1.2

Introduction

Ceci est un court tutoriel sur la programmation d'un Script-fu L'auteur va tenter de donner une courte introduction au monde des Scripts-fu et d'en montrer les principes de base. Nous allons créer un Script-fu complet pas à pas, le résultat final doit être un Script-fu qui produit un texte avec effet de métal. Je vais assumer que le lecteur possède les connaissances de base en Scheme, qui est le language de programmation utilisé pour les Scripts-fu.

Premiers pas sur la route des Scripts-fu

Premièrement nous allons essayer de faire avec le Gimp ce que nous souhaitons faire en Script-fu. C'est important parce que nous devons savoir exactement de quelles opérations nous aurons besoin et dans quel ordre elles devront se dérouler.

Pour commencer notre test, nous créons une image rgb et nous inscrivons en noir le mot "test" dans un calque transparent.

Maintenant on duplique le calque courant et on applique un gradient métal sur celui qui est le plus en avant.

Ensuite on sélectionne le 2ème calque et on l'élargit. Pour cela on utilise 'alpha vers sélection' et on agrandit de quelques pixels la sélection. maintenant on remplit la sélection avec le même gradient mais dans une autre direction.

Pour finir on rassemble les calques et on applique une ombre portée standard, et on applique un redimensionnement automatique pour éliminer les parties inutiles de l'image.

Reproduction des étapes dans la console Script-fu

D'accord, maintenant l'image ressemble à ce que nous souhaitions obtenir, voila ce que nous avons fait :

  1. Créé une nouvelle image
  2. Créé un calque texte
  3. Dupliqué le calque texte
  4. Elargi le calque texte du dessous
  5. Rempli les deux calques textes avec un gradient
  6. Rassemblé les deux calques
  7. Appliqué une ombre portée
  8. Rassemblé les deux calques courants
  9. Éliminé les zones en surplus (autocrop)

Notre tache est maintenant de rassembler ces instructions dans la console Script-fu. C'est là que nous allons entrer les commandes pour les tester. C'est aussi là que vous allez plus ou moins pouvoir debugger vos scripts et que vous pourrez tester les commandes Scripts-fu auxquelles vous n'êtes pas familier. La façon la plus simple d'écrire un Script-fu est de tester chaque ligne dans la console avant de l'inscrire dans le Script-fu si tout c'est passé comme désiré.

Pour cela, ouvrez la console par 'Exts->Script-fu->console...'. Si la console est ouverte, nous cliquons sur le bouton 'Parcourir...' en bas a gauche et nous accédons a la base de données des procédures. C'est là que nous pourrons chercher les fonctions qui sont accessibles depuis les Scripts-fu. A partir du nom de la fonction vous pouvez obtenir une description de son déroulement et les arguments attendus.

Maintenant il est temps de reproduire la première étape qui est la création de l'image, pour cela nous avons besoin de la fonction qui crée une image. Beaucoup de fonctions que vous trouverez dans la base de données ont le même nom dans les menus de Gimp. Après une petite recherche de 'new' dans la base, nous trouvons la fonction 'gimp-image-new' qui sonne comme ce que nous cherchons. Nous entrons la ligne suivante dans la console :

(gimp-image-new 256 256 RGB)

et nous obtenons en retour :

=> (gimp-image-new 256 256 RGB)

(3)

par exemple. La première ligne est simplement la commande exécutée, la deuxième est la valeur de retour de la fonction. Comme vous pouvez le lire dans la base de données des procédures, c'est l'id de l'image nouvellement créée.
(NDT: notez bien toutes les valeurs retournées par la console de script-fu et dans la suite de tutoriel remplacez systématiquement les valeurs d'id données pour l'exemple par celles que vous avez réellement obtenu; si vous avez obtenu (0) remplacez 3 par 0, etc...)
Vous pouvez utiliser l'id comme un simple argument à une fonction de la console. La valeur de retour est le premier piège de notre voyage au pays des Scripts-fu. Elle revient comme le premier élément d'une longue liste et non comme une valeur entière ! Maintenant pour l'utiliser nous avons à prendre le premier élément de la liste avec car, ce qui nous retournera l'image d'id 3.

Vous devez maintenant vous demander, où est l'image que nous avons créé, parce qu'on ne peut pas encore la voir. La réponse est que nous n'avons pas encore d'affichage pour elle, mais qu'elle réside déjà en mémoire. Nous allons donc créer un affichage pour elle. Nous essayons (gimp-display-new 3) mais nous récoltons une erreur :

ERROR: Procedural database execution failed:
    (gimp_display_new 3)

C'est juste un nouveau piège. Toutes les fonctions des Scripts-fu n'agissent pas comme dans les menus de Gimp. Souvent, elles sont plus 'bas-niveau' et vous avez plus de travail à faire pour obtenir les résultats escomptés. Le problème ici, aprés avoir créé une image, est qu'elle ne contient pas encore de calques. Pour afficher une image, elle doit avoir au moins un calque. Voici comment créer un calque :

=> (gimp-layer-new 3 256 256 RGB-IMAGE "foobar" 100 NORMAL-MODE)

(21)

Vous avez peut être constaté que les constantes RGB-IMAGE et NORMAL-MODE sont spécifiées dans la base de données comme étant RGB_IMAGE et NORMAL_MODE. Vous devez remplacer tous les _(underscore) par des -(signe 'moins') dans le monde des constantes Script-fu. Si vous n'êtes pas sûr de vos remplacements, testez-les dans la console Script-fu.

L'étape suivante est d'ajouter le nouveau calque à la nouvelle image, comme décrit dans la base de données :

(gimp-image-add-layer 3 21 0)

et de finalement afficher notre image :

=> (gimp-display-new 3)

(4)

L'image nouvellement affichée à l'air un peu troublée, parce qu'elle est remplie de couleurs aléatoires, pour les effacer nous allons remplir cette image avec la couleur de fond courante :

=> (gimp-drawable-fill 21 BG-IMAGE-FILL)
()

La fonction pour remplir un calque aurait peut-être pu s'appeller gimp-layer-fill, mais un calque est une zone de dessin, et elle s'appelle gimp-drawable-fill. Donc pour trouver des fonctions qui se rapportent à des calques, vous devez chercher des fonctions qui se rapportent à des zones de dessin.

Maintenant notre image est initialisée. Trop rapidement pour vous ? Laissez moi résumer et écrire notre première fonction scheme. D'abord encapsulons le tout dans une clause let* et plaçons notre let* dans une fonction.

(define (my-make-new-image)
 (let* ((image (car (gimp-image-new 256 256 RGB)))
        (layer (car (gimp-layer-new image 256 256
                     RGB-IMAGE "foobar" 100 NORMAL-MODE))))
       (gimp-drawable-fill layer BG-IMAGE-FILL)
       (gimp-image-add-layer image layer 0)
       (gimp-display-new image)
       image))

Dans un script scheme la dernière ligne est la valeur de retour de la fonction, image est donc la valeur de retour de notre fonction.

Retournons à notre affaire de texte métallisé. Nous avons besoin d'ajouter un texte et de jouer avec lui. En cherchant rapidement dans la base nous trouvons gimp-text-fontname. Pour l'appeller nous utiliserons :

=> (gimp-text-fontname 3 21 -1 0 "Foobar" 0 TRUE 25 PIXELS
                    "-freefont-blippo-heavy-r-normal-*-24-*-*-*-p-*-iso8859-1")

(33)

Voila, notre texte est installé dans un nouveau calque transparent. La chaîne "-freefont-blippo-heavy-r-normal-*-24-*-*-*-p-*-iso8859-1" doit vous sembler être de la magie noire, mais nous allons plus tard découvrir un moyen simple de la générer.
(NDT : Pour obtenir satisfaction il faut impérativement que la police blippo soit installée sur votre serveur graphique X. C'est peu probable mais il y a une solution pour entrer une police valide. Utilisez Gimp et créez votre texte de façon classique. Puis, dans l'outil Texte, cliquez sur l'onglet Information sur la police, et récupérez l'information fournie dans le champ : Nom de la police demandée, pour la réintroduire dans la console de script-fu).

Maintenant nous avons besoin de copier ce calque, vous remarquerez qu'il n'y a pas de fonction de duplication dans la base, nous aurons donc besoin de la créer manuellement.

(define (my-duplicate-layer image layer)
        (let* ((dup-layer (car (gimp-layer-copy layer 1))))
              (gimp-image-add-layer image dup-layer 0)
              dup-layer))

Et de l'invoquer :

=> (my-duplicate-layer 3 21)

41

L'étape suivante est d'ajouter de l'espace autour du texte. gimp-layer-resize va nous y aider.

(gimp-layer-resize 41 200 100 5 5)

Cela fonctionne, mais les tailles sont données en dur. Voici une technique plus flexible :

(define (my-layer-add-border layer border)
        (let* ((width  (car (gimp-drawable-width  layer)))
               (height (car (gimp-drawable-height layer))))
               (gimp-layer-resize layer
                                  (+ width border) (+ height border)
                                  (/ border 2) (/ border 2))))

Maintenant nous devons passer la transparence du calque en sélection, et agrandir cette dernière.

(gimp-selection-layer-alpha 41)
(gimp-selection-grow 3 5)

Avant de remplir le calque, nous avons besoin de décocher la case 'préserver la transparence' qui fait que tous les espaces transparents, le restent.

(gimp-layer-set-preserve-trans 41 0)

Maintenant pour remplir la sélection avec un gradient :

(gimp-blend 41 CUSTOM NORMAL LINEAR 100 0 REPEAT-NONE FALSE 0 0 0 0 30 50)

L'écriture du Script-fu à proprement parler

Maintenant nous avons tout pour faire tourner notre script, reste juste à attacher les pièces entre elles. Dans les étapes suivantes nous allons supprimer les actions inutiles et probablement ajouter des éléments.

D'accord comment enregistrer un Script-fu ? Vous aurez besoin de 3 choses : un fichier dans lequel le script est écrit, ce fichier devra étre placé dans le répertoire ~/.gimp-version/scripts/. Une fonction, qui effectue toutes les manipulations, et pour finir un appel à script-fu-register pour rendre le script visible depuis le GIMP.

define (my-dummy-function a b)
  (print "Do nothing"))

(script-fu-register "my-dummy-function"
                    _"/Xtns/Script-Fu/My Stuff/Dummy..."
                    "Do nothing"
                    "Joey User"
                    "Joey User"
                    "August 2000"
                    ""
                    SF-ADJUSTMENT _"Width" '(400 1 2000 1 10 0 1)
                    SF-ADJUSTMENT _"Height" '(30 1 2000 1 10 0 1))

En suivant le menu Exts->Script-fu->Rafraichir le fichier sera lu par le GIMP et sa fonction sera rendue accessible depuis le menu de GIMP.

Attention, Il y a 2 sortes de Script-fu : La première sorte crée une nouvelle image, et est accessible par Exts->Script-fu->... et la 2ème va manipuler une image existante, et sera accessible par un clic droit sur l'image à manipuler. Pour enregistrer la 2ème sorte de Script-fu vous aurez besoin d'ajuster le chemin <Image>/Script-fu/... et de prendre (au moins) 2 arguments SF-IMAGE et SF-DRAWABLE.

Ce qui nous amène au sujet suivant, les arguments que peut prendre votre fonction, et leurs types

Type de l'argument Type de donnée Description
SF-IMAGE Entier (id d'une image) Utilisé pour récupérer l'id d'une image
SF-DRAWABLE Entier (id d'une zone de dessin) Récupère l'id d'une zone de dessin
SF-VALUE Chaîne de caractères Entrée d'une valeur numérique
SF-TOGGLE Case à cocher (TRUE or FALSE) Reçoit une valeur vraie ou fausse
SF-PATTERN Chaîne de caractères (nom d'un motif) Vous laisse sélectionner un motif
SF-ADJUSTMENT Liste (valeur-de-départ valeur-min valeur-max petit-pas grand-pas [entier=0 or flottant=1] [slider=0 or roll-box=1]) Crée un "slider" ou une "input box" avec les valeurs données
SF-FILENAME Chaîne de caractères (chemin) Vous laisse parcourir à la recherche du fichier
SF-STRING Chaîne de caractère Vous demande une Chaîne dans une boite de dialogue
SF-FONT Chaîne de caractères (nom d'une police de caractères) Vous laisse choisir une police
SF-COLOR Liste (Rouge Vert Bleu) [0-255] Vous laisse choisir une couleur
SF-OPTION Liste de chaînes de caractères Vous laisse choisir un élément dans une liste
SF-GRADIENT Chaîne de caractère (nom du gradient) Vous laisse choisir un dégradé

Le code suivant crée une boite avec tous les types d'entrées possibles.

(define (my-demo-box value adj1 adj2 image drawable toggle pattern string font color option gradient)
  (print "Do nothing"))

(script-fu-register "my-demo-box"
                    "/Xtns/Script-Fu/My Stuff/Demo Box..."
                    "Do nothing"
                    "Joe User"
                    "Joe User"
                    "August 2000"
                    ""
                    SF-ADJUSTMENT "SF-ADJUSTMENT (slider)" '( 30 1 2000 1 10 1 0)
                    SF-ADJUSTMENT "SF-ADJUSTMENT"         '(400 1 2000 1 10 1 1)
                    SF-COLOR      "SF-COLOR" '(255 0 255)
                    SF-DRAWABLE   "SF-DRAWABLE" 0
                    SF-FONT       "SF-FONT" ""
                    SF-GRADIENT   "SF-GRADIENT"  "Golden"
                    SF-IMAGE      "SF-IMAGE" 0
                    SF-OPTION     "SF-OPTION" '("Option 1" "Option 2" "Option 3")
                    SF-PATTERN    "SF-PATTERN" "Wood"
                    SF-STRING     "SF-STRING" "Test String"
                    SF-TOGGLE     "SF-TOGGLE" TRUE
                    SF-VALUE      "SF-VALUE" "0"
                    SF-FILENAME   "SF-FILENAME" "/")

La boite résultante ressemblera à ça :

Retournons à nos affaires, et finissons notre exemple de Script-fu. Décidons des arguments dont nous avons besoin en fonction de ce que nous utilisons : un text (SF-STRING) une police (SF-FONT) et une taille pour la police (SF-VALUE).

L'enregistrement par le script-fu-register vous est laissé comme exercice. Si vous ne souhaitez pas le faire seul, inspirez vous de la commande script-fu-register quelques lignes plus haut.

Maintenant que notre enregistrement est préparé, nous pouvons remplir la fonction avec du code. D'abord nous insérons nos raccourcis my-duplicate-layer et my-layer-add-border en haut du fichier. Ensuite nous allons plus ou moins copier coller nos expérimentations Script-fu en Console. Nous déplaçons l'appel à gimp-display-new en fin de fonction, ainsi nous ne verrons pas l'image avant la fin du travail. Nous ajoutons d'autres fonctions d'aide, qui vont finalement découper notre image à la bonne dimension. Le résultat doit ressembler à ça :

(define (my-image-to-layer-size image layer)
  (gimp-layer-set-offsets layer 0 0)
  (gimp-image-resize image
                     (car (gimp-drawable-width layer))
                     (car (gimp-drawable-height layer))
                     0 0))

(define (my-layer-add-border layer border)
        (let* ((width  (car (gimp-drawable-width  layer)))
               (height (car (gimp-drawable-height layer))))
               (gimp-layer-resize layer
                                  (+ width border) (+ height border)
                                  (/ border 2) (/ border 2))))

(define (my-duplicate-layer image layer)
        (let* ((dup-layer (car (gimp-layer-copy layer 1))))
              (gimp-image-add-layer image dup-layer 0)
              dup-layer))

(define (my-make-metal-font text font font-size)
 (let* ((image (car (gimp-image-new 256 256 RGB)))
        (bottom-font-layer (car (gimp-text-fontname image -1 0 0 text 0 TRUE font-size PIXELS
                                             font))))

   (let* ((top-font-layer (my-duplicate-layer image bottom-font-layer)))

     (my-layer-add-border bottom-font-layer 20)

     (gimp-layer-set-preserve-trans bottom-font-layer 0)
     (gimp-gradients-set-active "Four_bars")

     (gimp-sélection-layer-alpha bottom-font-layer)
     (gimp-sélection-grow image 3)
     (gimp-blend bottom-font-layer CUSTOM NORMAL LINEAR 100 0 REPEAT-NONE FALSE 0 0 0 0 200 50)


     (gimp-sélection-layer-alpha top-font-layer)
     (gimp-blend top-font-layer    CUSTOM NORMAL LINEAR 100 0 REPEAT-NONE FALSE 0 0 0 50 200 0)
     (gimp-sélection-clear image)

     (let* ((new-layer (car (gimp-image-merge-visible-layers
                             image EXPAND-AS-NECESSARY))))
       (my-image-to-layer-size image new-layer))

     (gimp-display-new image))
   ))

(script-fu-register "my-make-metal-font"
                    "/Xtns/Script-Fu/Logos/Metal Font..."
                    "Create an example image of a custom gradient"
                    "Federico Mena Quintero"
                    "Federico Mena Quintero"
                    "June 1997"
                    ""
                    SF-STRING     "Text" "The GIMP"
                    SF-FONT       "Font" "-*-blippo-*-r-*-*-24-*-*-*-p-*-*-*"
                    SF-VALUE      "Font Size" "50" )

Les piéges les plus courants

Voici la liste des piéges dans lesquels j'espére ne pas vous voir tomber :

Les valeurs de retour

Quand une fonction retourne une valeur, elle est toujours encapsulée dans une liste, qu'elle soit constituée d'une valeur ou plusieurs. Même si la valeur elle même est une liste (comme pour une couleur, liste des 3 valeurs RGB retournée par gimp-color-picker) elle sera encapsulée dans une autre liste.

Si plusieurs valeurs sont retournées, elles sont alors dans une liste, la première valeur de retour étant le premier élément, la 2ème valeur le 2ème élément, etc... Donc vous devez toujours appliquer la fonction scheme car au résultat pour récupérer la valeur correcte.

La "Compliance" R5RS

R5RS décrit le language scheme, tristement le scheme de Gimp n'est pas conforme à cette norme. C'est parce que Gimp utilise SIOD comme interprète scheme. SIOD est un petit interprète scheme, et ne produit qu'une petite partie des spécifications R5RS, et certaines portent un autre nom que dans la version R5RS. Par exemple on n'a pas display mais on a print. Pour avoir un aperçu des fonctions scheme disponibles dans Gimp vous pouvez consulter la page de SIOD. Pire, Gimp ne donne pas accès a toutes les fonctions décrites sur cette page, qui reste néanmoins un bon point de départ. Vous pouvez utiliser la fonction apropos en console pour plus d'infos sur une fonction spécifique.

=> (apropos "write")

(fwrite writes swrite write-mask stroke-overwrite)

Déverminage

Quand vous commencez à travailler au corps les Scripts-fu et même si vous y êtes familier, il arrive souvent que le script ne se passe pas comme prévu. Pire parfois le message d'erreur renvoyé est inexistant ou incompréhensible. La plupart du temps la cause est simple et la recherche difficile. Les étapes suivantes vont vous la rendre plus facile.

D'abord regardez si votre script est syntaxiquement correct, c'est à dire que vous avez fermé toutes vos parenthèses et guillemets. Un éditeur avec support syntaxique des indentations et des parenthèses, comme emacs, vous sera utile.

Vous pouvez aussi avoir omis d'utiliser un car sur une valeur de retour. Pour vérifier cela copiez collez votre code dans la console et appellez le manuellement.

ERROR: Invalid types specified for arguments

L'erreur ci dessus en est révélatrice.

Vous pouvez aussi appeller une fonction avec un mauvais nombre d'arguments, ce qui vous amènera le message d'erreur suivant :

> (gimp-image-get-layers)
ERROR: too insomniark niarkfew arguments (see errobj)

(NDT : vous pouvez aussi avoir 2 fonctions du méme nom, mais avec des paramètres différents. Cela ne sera pas détecté par gimp comme une erreur mais déclenchera néamoins une erreur à l'éxécution).

En consultant l'erreur, vous pourrez voir quels arguments étaient requis et combien vous en avez donné :

=> (print errobj)

((image))
()

A la fin si votre fonction ne marche toujours pas, ajoutez quelques appels a print aux bons endroits, pour vérifier jusqu'où le déroulement se passe correctement. pour voir cette sortie, exécutez votre travail en console.

Les choses qu'on ne peut pas faire dans un Script-fu

Jusqu'ici j'ai décris ce qu'on peut faire avec un Script-fu, mais il y a pleins de choses impossibles.

La première chose est que vous pouvez vouloir donner de meilleures valeurs par défaut aux arguments que votre fonction va recevoir. On peut uniquement passer des constantes en arguments à un Script-fu. Vous pouvez vouloir passer par exemple la moitié de la taille d'une image, eh bien ce n'est pas possible.

Une autre contrainte, est l'interface qui est apportée par l'ensemble des SF-* que vous utiliserez. Vous ne pourrez pas en avoir d'autre sauf si vous décider de faire des plug-ins (greffons) et d'abandonner les Scripts-fu.

Les Scripts-fu sont plus lent que les plug-ins en général, surtout pour le traitement d'image pixel par pixel.

Vous souhaiterez peut être ouvrir toutes les images contenues dans un répertoire. La fonction opendir de SIOD ne fonctionne pas.

=> (opendir "/")

ERROR: unbound variable (errobj opendir)

Une solution consiste à placer le résultat d'un ls de ce répertoire dans un fichier, et de lire ce fichier, ou bien vous pouvez écrire Perl-Fu, Python-Fu ou un Plug-in.

Exemples

C'est souvent une bonne idée de regarder les Scripts-fu des autres pour se faire une idée. les Scripts-fu peuvent par exemple être trouvés dans le répertoire usr/share/gimp/version-of-gimp/scripts/. Il y a des liens sur des sites de Script-fu sur la page officielle de gimp mes propres scripts sont disponibles ici mais attention, ils ne sont ni sûrs ni documentés.

References

Traduction Septembre 2001 Laetitia MARIN