Gimp

Installation
Serveur de fontes
Utilisation
Squelette d'un fichier
Débuggage
Le nom des fonctions
Coloriage et dégradés
Sélections
Exemples de fonds
Fontes
Arguments
Arguments en ligne de commande
Écrire un fichier sur le disque
Lire un fichier
Texte en provenance de LaTeX
Masques
Transformation du blanc en transparent
Fontes : divers effets
Installer un Plug-in
Animations
Recoller deux images
PDL

Installation

Installer une version récente de Perl (sans aucune option de développement, comme les Threads), les modules Gtk, PDL, une version récente de Gimp (ici, 1.1.26), et le module Gimp si nécessaire.

Serveur de fontes

Récupérer des fontes PostScript ou True Type

Dans chaque répertoire contenant des fontes PostScript, taper

  type1inst
  mkfontdir

Dans chaque répertoire contenant des fontes True Type, taper

  ttmkfdir > fonts.scale
  mkfontdir

Si l'on est sur un terminal X, lancer un serveur de fontes (in faut une version récente de xfs, qui reconnaisse les fontes TTF)

  xfs -config xfs_config -port 2365 &
  xset +fp tcp/`hostname`:2365
  xset fp rehash

où le fichier xfs_config contient des choses comme :

  client-limit = 10
  clone-self = on
  catalogue = /share/nfs/users1/umr-tge/zoonek/spool/GIMP_FONTS/freefont, /share/nfs/users1/umr-tge/zoonek/spool/GIMP_FONTS/sharefont, /share/nfs/users1/umr-tge/zoonek/spool/GIMP_FONTS/TTF
  default-point-size = 120
  default-resolutions = 100,100,75,75
  use-syslog = off

Utilisation

Si nécessaire (sur un TX), lancer le serveur de fontes.

Lancer Gimp

  unset LANG LC_TYPE 
  gimp &

Lancer le serveur Perl

  Xtns-->Perl-->Server

Lancer un script en Perl

  perl 1.pl

Squelette d'un fichier

#! perl -w
use strict;

use Gimp;
use Gimp::Fu;
use Gimp::Util;

# Commandes de débuggage : tous les appels de fonctions 
# de la PDB (Oricedural Data Base) seront affichés, 
# avec leurs arguments et leur sugnification.
#Gimp::set_trace(TRACE_NAME);
Gimp::set_trace(TRACE_ALL);

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [],
  sub {

# Créer une nouvelle image
    my $image = new Image(256, 256, RGB);

# Faire attention à la fonction Undo
    Gimp->gimp_undo_push_group_start($image);

# Créer un calque dans l'image (il en faut au moins un)
# et ne pas oublier de l'attacher à l'image.
# Il est généralement judicieux de colorier le calque, 
# sinon, il peut contenir n'importe quoi.
    my $background = $image->layer_new($image->width, $image->height, 
                                  RGB_IMAGE, 
                                  "Background", 
                                  100, NORMAL_MODE);
    $image->add_layer($background, 0);
    $background->drawable_fill(BG_IMAGE_FILL);

# On peut créer un autre calque.
# Celui-ci contient un « channel alpha » et sera transparent. 
    my $layer = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "first layer",
                  100, NORMAL_MODE);
    $image->add_layer($layer, 0);
    $layer->fill(TRANS_IMAGE_FILL);

# On fait ce que l'on a à faire avec l'image
    ...

# On réactive la commande Undo
    Gimp->gimp_undo_push_group_end($image); 

# On renvoie l'image nouvellement créée (qui va s'afficher)
    return $image;
  };

# Ne pas oublier cette dernière ligne !!!
# (Si le script s'exécute sans donner d'erreur mais sans qu'aucune
# image ne s'affiche, vous l'avez oubliée.)
exit main;

Débuggage

Voici à quoi ressemblent les instructions de débuggage

gimp_image_new(
	INT32 width=256	"The width of the image"
	INT32 height=256	"The height of the image"
	INT32 type=0	"The type of image: { RGB (0), GRAY (1), INDEXED (2) }"
	) = (
	IMAGE image=8	"The ID of the newly created image"
	)
gimp_undo_push_group_start(
	IMAGE image=8	"The ID of the image in which to pop an undo group"
	) = (
	)
gimp_palette_get_foreground(
	) = (
	COLOR foreground=[255,255,0]	"The foreground color"
	)
gimp_palette_get_background(
	) = (
	COLOR background=[255,0,0]	"The background color"
	)
gimp_palette_set_foreground(
	COLOR foreground=[255,255,0]	"The foreground color"
	) = (
	)
gimp_palette_set_background(
	COLOR background=[255,0,0]	"The background color"
	) = (
	)
gimp_image_width(
	IMAGE image=8	"The image"
	) = (
	INT32 width=256	"The image's width"
	)
gimp_image_height(
	IMAGE image=8	"The image"
	) = (
	INT32 height=256	"The image's height"
	)
gimp_layer_new(
	IMAGE image=8	"The image to which to add the layer"
	INT32 width=256	"The layer width: (0 < width)"
	INT32 height=256	"The layer height: (0 < height)"
	INT32 type=0	"The layer type: { RGB_IMAGE (0), RGBA_IMAGE (1), GRAY_IMAGE (2), GRAYA_IMAGE (3), INDEXED_IMAGE (4), INDEXEDA_IMAGE (5) }"
	STRING name="Background"	"The layer name"
	FLOAT opacity=100.000000	"The layer opacity: (0 <= opacity <= 100)"
	INT32 mode=0	"The layer combination mode: { NORMAL_MODE (0), DISSOLVE_MODE (1), BEHIND_MODE (2), MULTIPLY_MODE (3), SCREEN_MODE (4), OVERLAY_MODE (5), DIFFERENCE_MODE (6), ADDITION_MODE (7), SUBTRACT_MODE (8), DARKEN_ONLY_MODE (9), LIGHTEN_ONLY_MODE (10), HUE_MODE (11), SATURATION_MODE (12), COLOR_MODE (13), VALUE_MODE (14), DIVIDE_MODE (15) }"
	) = (
	LAYER layer=25	"The newly created layer"
	)
gimp_image_add_layer(
	IMAGE image=8	"The image"
	LAYER layer=25	"The layer"
	INT32 position=0	"The layer position"
	) = (
	)
gimp_drawable_fill(
	DRAWABLE drawable=25	"The drawable"
	INT32 fill_type=1	"The type of fill: FG_IMAGE_FILL (0), BG_IMAGE_FILL (1), WHITE_IMAGE_FILL (2), TRANS_IMAGE_FILL (3), NO_IMAGE_FILL (4)"
	) = (
	)
gimp_blend(
	DRAWABLE drawable=25	"The affected drawable"
	INT32 blend_mode=0	"The type of blend: { FG_BG_RGB (0), FG_BG_HSV (1), FG_TRANS (2), CUSTOM (3) }"
	INT32 paint_mode=0	"The paint application mode: { NORMAL_MODE (0), DISSOLVE_MODE (1), BEHIND_MODE (2), MULTIPLY_MODE (3), SCREEN_MODE (4), OVERLAY_MODE (5), DIFFERENCE_MODE (6), ADDITION_MODE (7), SUBTRACT_MODE (8), DARKEN_ONLY_MODE (9), LIGHTEN_ONLY_MODE (10), HUE_MODE (11), SATURATION_MODE (12), COLOR_MODE (13), VALUE_MODE (14), DIVIDE_MODE (15) }"
	INT32 gradient_type=0	"The type of gradient: { LINEAR (0), BILINEAR (1), RADIAL (2), SQUARE (3), CONICAL_SYMMETRIC (4), CONICAL_ASYMMETRIC (5), SHAPEBURST_ANGULAR (6), SHAPEBURST_SPHERICAL (7), SHAPEBURST_DIMPLED (8), SPIRAL_CLOCKWISE (9), SPIRAL_ANTICLOCKWISE (10) }"
	FLOAT opacity=100.000000	"The opacity of the final blend (0 <= opacity <= 100)"
	FLOAT offset=0.000000	"Offset relates to the starting and ending coordinates specified for the blend. This parameter is mode dependent (0 <= offset)"
	INT32 repeat=0	"Repeat mode: { REPEAT_NONE (0), REPEAT_SAWTOOTH (1), REPEAT_TRIANGULAR (2) }"
	INT32 supersample=0	"Do adaptive supersampling (TRUE or FALSE)"
	INT32 max_depth=0	"Maximum recursion levels for supersampling"
	FLOAT threshold=0.000000	"Supersampling threshold"
	FLOAT x1=0.000000	"The x coordinate of this blend's starting point"
	FLOAT y1=0.000000	"The y coordinate of this blend's starting point"
	FLOAT x2=255.000000	"The x coordinate of this blend's ending point"
	FLOAT y2=255.000000	"The y coordinate of this blend's ending point"
	) = (
	)
gimp_undo_push_group_end(
	IMAGE image=8	"The ID of the image in which to pop an undo group"
	) = (
	)

Le nom des fonctions

Pour avoir le nom d'une fonction, on cherche dans la PDB.

Xtns-->DB Browser

*

Une fois qu'on a le nom de la fonction, on peut l'appeler de différentes manières en Perl. Prenons l'exemple de la fonction

  gimp_blend(drawable,
             blend_mode,
             paint_mode,
	     gradient_type,
             opacity,
             offset,
             repeat,
             supersample,
             max_depth,
             threshold,
             x1, y1, x2, y2)

On peut l'appeler des manières suivantes.

  Gimp->gimp_blend($drawable, $blend_mode, ...);
  $drawable->blend($blend_mode, ...);

Beaucoup de fonctions agissent sur un drawable mais demandent en argument à la fois une image et un drawable, sans pour autant utiliser l'image : on peut l'omettre.

Certaines fonctions (en particulier les plug-ins) attendent aussi un premier argument, spécifiant si la fonction est appelée interactivement ou non : il ne faut pas le mettre. C'est par exemple le cas de la fonction

  plug_in_plasma(
        run_mode,
        image,
        drawable,
        seed,
        turbulence);

On peut l'invoquer des manières suivantes.

  Gimp-->plug_in_plasma($image, $drawable, $seed, $turbulence);   
  Gimp-->plug_in_plasma($drawable, $seed, $turbulence);   
  $drawable->plug_in_plasma($seed, $turbulence);

Coloriage et dégradés

Les différentes commandes fill permettent de colorier l'image (ou la partie sélectionnée d'une image) à l'aide d'une couleur ou d'un motif.

  gimp_bucket_fill
  gimp_drawable_fill
  gimp_edit_fill

La commande blend permet de colorier avec un dégradé (linéaire, circulaire, en dents de scie, etc.)

  gimp_blend

Voici un exemple complet.

#! perl -w
use strict;

use Gimp;
use Gimp::Fu;
use Gimp::Util;

# Commandes de débuggage : tous les appels de fonctions 
# de la PDB (Oricedural Data Base) seront affichés, 
# avec leurs arguments et leur sugnification.
#Gimp::set_trace(TRACE_NAME);
Gimp::set_trace(TRACE_ALL);

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [],
  sub {

# Créer une nouvelle image
    my $image = new Image(256, 256, RGB);

# Faire attention à la fonction Undo
    Gimp->gimp_undo_push_group_start($image);

# On sauvegarde les couleur du tracé et de fond, 
# car on va les modifier.
    my $fg = Palette->get_foreground();
    my $bg = Palette->get_background();

    Palette->set_foreground("yellow");
    Palette->set_background([255,0,0]);

# Créer un calque dans l'image (il en faut au moins un)
# et ne pas oublier de l'attacher à l'image.
# Il est généralement judicieux de colorier le calque, 
# sinon, il peut contenir n'importe quoi.
    my $background = $image->layer_new($image->width, $image->height, 
                                  RGB_IMAGE, 
                                  "Background", 
                                  100, NORMAL_MODE);
    $image->add_layer($background, 0);
    $background->drawable_fill(BG_IMAGE_FILL);

# On fait ce que l'on a à faire avec l'image
    $background->blend(FG_BG_RGB, NORMAL_MODE, LINEAR, 
                       100, 0, REPEAT_NONE, 
                       0, 0, 0, 
                       0, 0, 255, 255);

# On réactive la commande Undo
    Gimp->gimp_undo_push_group_end($image); 

# On restaure les couleurs
    Palette->set_foreground($fg);
    Palette->set_background($bg);

# On renvoie l'image nouvellement créée (qui va s'afficher)
    return $image;
  };
exit main;

*

Sélections

On peut sélectionner une partie rectangulaire de l'image, une partie circulaire (avec anti-aliasing), avec éventuellement un « dégradé de transparence » le long du bors de la sélection (feather).

    gimp_rect_select
    gimp_ellipse_select

Il est aussi possible de sélectionner une zône quelconque, délimitée par une famille de points, ou de sélectionner une zône de manière « intelligente » (fuzzy) en fonction des couleurs.

    gimp_free_select
    gimp_fuzzy_select

Voici deux exemples de sélections imbriquées les unes dans les autres.

  my $max = 10;
  for(my $i=0; $i<$max; $i++){
    $image->rect_select( $image->width/$max/2*$i, $image->height/$max/2*$i,
                         ($max-$i)/$max*$image->width, ($max-$i)/$max*$image->height,
                         REPLACE, 0, 0);
    $background->blend(FG_BG_RGB,
                       NORMAL_MODE, LINEAR,
                       100, 0, REPEAT_NONE,
                       0, 0, 0,
                       ($image->selection_bounds)[1..4]);
    my ($a, $b) = (Palette->get_foreground, Palette->get_background);
    Palette->set_background($a);
    Palette->set_foreground($b);
  }
  $image->selection_clear;

*

  $image->ellipse_select( $image->width/$max/2*$i, $image->height/$max/2*$i,
                         ($max-$i)/$max*$image->width, 
                         ($max-$i)/$max*$image->height,
                         REPLACE, 
                         1, # Antialiasing
                         0, 0);

*

Exemples de fonds

Créer des « nuages » (plasma) et éclaircir l'image (levels).

  $background->plug_in_plasma(1236, 2);
  $background->levels(VALUE_LUT,
                      0, 255,    # In
                      1,         # Gamma correction 
                      150, 255); # Out

*

Dessiner des nuages (plasma) et les rendre un peu flous (gauss_iir). Dans un autre calque, créer d'autres nuages (plasma), les faire passer en noir et blanc (desaturate) (peut-être pas nécessaire), et utiliser ce calque pour cabosser le premier (bump_map). Le second calque n'étant plus nécessaire, on peut l'effacer (remove_layer).

    $background->plug_in_plasma(1236, 2);
    $background->plug_in_gauss_iir(50,1,1);
    my $bump = $image->layer_new($image->width, $image->height,
                                 RGBA_IMAGE,
                                 "bumps",
                                 100, NORMAL_MODE);
    $image->add_layer($bump,0);
    $bump->fill(TRANS_IMAGE_FILL);
    $bump->plug_in_plasma(1234,2);
    $bump->desaturate();
    $background->plug_in_bump_map($bump, 135, 45,
                                  10, # Profondeur, 
                                  0, 0, 0, 0,
                                  1, # Compensate for darkening
                                  0, LINEAR);
    $bump->remove_layer();

*

Fontes

Il convient tout d'abord d'avoir le nom de la fonte que l'on va utiliser, par exemple à l'aide d'une des deux commandes suivantes.

    xlsfonts | less
    xfontsel

On peut ajouter du texte (text_fontname) à un calque déjà existant, mais on n'obtient qu'une sélection flottante, qu'il faut ensuite accrocher (anchor) à ce calque.

# Création d'un nouveau calque (au dessus du fond), pour le texte.
# Celui-ci contient un « channel alpha » et sera transparent. 
    my $layer = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "first layer",
                  100, NORMAL_MODE);
    $image->add_layer($layer, 0);
    $layer->fill(TRANS_IMAGE_FILL);

# Ajout du texte
    my $fontname = "-paradise-arnoldboecklin-extrabold-r-normal--0-0-0-0-p-0-iso8859-1";
    $layer->text_fontname(20, 20, "ABC",
                          0, # border
                          1, # antialiasing
                          100, PIXELS,
                          $fontname) -> anchor;

    Gimp->gimp_undo_push_group_end($image); 
    Palette->set_foreground($fg);
    Palette->set_background($bg);

    return $image;
  };

*

En fait, il n'est pas nécessaire de créer un calque, Gimp peut s'en charger tout seul, si on précise « -1 » comme numéro de calque (mais comme cela me pose d'autres problèmes, j'éviterai cette solution).

    my $layer = $image->text_fontname(-1,
                                      20, 20, "ABC",
                                      0, 1, 100, PIXELS,
                                      $fontname);
    $layer->set_name("Text layer");

Il est possible de récupérer la taille du texte avant de le tracer, par exemple pour le centrer.

    my($w,$h) = Gimp->gimp_text_get_extents_fontname("ABC",100,PIXELS,$fontname);
    $layer->text_fontname(($image->width-$w)/2,
                          ($image->height-$h)/2, "ABC",
                          0, 1, 100, PIXELS,
                          $fontname);

*

Voici un exemple un peu moins trivial : on a rajouté une ombre.

    my $shadow = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "first layer",
                  100, NORMAL_MODE);
    $image->add_layer($shadow, 0);
    $shadow->fill(TRANS_IMAGE_FILL);

    my($w,$h) = Gimp->gimp_text_get_extents_fontname("ABC",100,PIXELS,$fontname);
    $shadow->text_fontname(($image->width-$w)/2,
                         ($image->height-$h)/2,
                         "ABC",
                         0, # border
                         1, # antialiasing
                         100, PIXELS,
                         $fontname) -> anchor;

    $shadow->set_name("Shadow");
    $image->selection_clear;
    my $text = $shadow->copy(1);
    $image->add_layer($text,-1);
    $text->set_name("Text");
    $shadow->plug_in_gauss_iir(10,1,1);
    $shadow->set_offsets(10,10);
    # On colorie le texte en rouge.
    $text->set_preserve_trans(1);
    $image->selection_all();
    Palette->set_foreground("red");
    $text->bucket_fill(FG_IMAGE_FILL,NORMAL_MODE,100,0,0,0,0);

*

Arguments

Ces scripts peuvent accepter divers arguments, qui pourront être demandés à l'utilisateur de manière interactive.

register
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [[PF_RADIO, "choose", "Choose one of these", 0, [A=>1, B=>2, C=>3]],
   [PF_VALUE, "number", "Give me a number", 50],
   [PF_SLIDER, "number", "Yet another number", 0, [-100,100,1]],
   [PF_INT8,  "number", "again...", 0],
   [PF_COLOR, "color", "Choose a color", [255,0,0]],
   [PF_TOGGLE, "twice", "Do it twice?" , 0],
   [PF_FONT, "font", "What font type to use - size will be ignored", $fontname],
   [PF_STRING, "string", "Text string to typeset", "ABCabc"],
   [PF_DRAWABLE, "source", "What drawable shall we use?"],
   [PF_INT32, "angle", "Angle, 0 is left", 120],
  ],
  sub {
    print "Les arguments étaient les suivants\n";
    my $i=0;
    while( scalar @_ ){
      $i++;
      $_=shift @_;
      print "$i: $_\n";
    }
  };
exit main;

*

Reprenons le même programme, avec comme arguments, le texte à imprimer et comme arguments facultatifs la couleur et la fonte.

#! perl -w
use strict;

use Gimp;
use Gimp::Fu;
use Gimp::Util;

Gimp::set_trace(TRACE_ALL);

my $fontname = "-paradise-arnoldboecklin-extrabold-r-normal--0-0-0-0-p-0-iso8859-1";

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [
   [PF_STRING, "texte", "texte à écrire", "ABC"],
   [PF_COLOR, "couleur", "couleur du texte", [255,0,0]],
   [PF_FONT, "fonte", "fonte de caractères à utiliser", $fontname],
   [PF_SLIDER, "taille", "taille du texte", 100, [10,300,1]],
  ],
  sub {
    my ($t, $col, $f, $size) = @_;
    my $image = new Image(256, 256, RGB);
    Gimp->gimp_undo_push_group_start($image);
    my $fg = Palette->get_foreground();
    my $bg = Palette->get_background();

    Palette->set_foreground("black");
    Palette->set_background("white");

    my $background = $image->layer_new($image->width, $image->height, 
                                  RGB_IMAGE, 
                                  "Background", 
                                  100, NORMAL_MODE);
    $image->add_layer($background, 0);
    $background->drawable_fill(BG_IMAGE_FILL);

# Colorier le fond
    $background->plug_in_plasma(1236, 2);
    $background->plug_in_gauss_iir(50,1,1);
    my $bump = $image->layer_new($image->width, $image->height,
                                 RGBA_IMAGE,
                                 "bumps",
                                 100, NORMAL_MODE);
    $image->add_layer($bump,0);
    $bump->fill(TRANS_IMAGE_FILL);
    $bump->plug_in_plasma(1234,2);
    $bump->desaturate();
    $background->plug_in_bump_map($bump, 135, 45, 
                                  10, # Profondeur, 
                                  0, 0, 0, 0, 
                                  1, # Compensate for darkening
                                  0, LINEAR);
    $bump->remove_layer();
    $background->levels(VALUE_LUT, 
                        0, 255,    # In
                        1,         # Gamma correction 
                        150, 255); # Out

    my $shadow = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "first layer",
                  100, NORMAL_MODE);
    $image->add_layer($shadow, 0);
    $shadow->fill(TRANS_IMAGE_FILL);

    my($w,$h) = Gimp->gimp_text_get_extents_fontname($t,$size,PIXELS,$f);
    $shadow->text_fontname(($image->width-$w)/2,
                         ($image->height-$h)/2, 
                         "ABC",
                         0, # border
                         1, # antialiasing
                         100, PIXELS,
                         $fontname) -> anchor;

    $shadow->set_name("Shadow");
    $image->selection_clear;
    my $text = $shadow->copy(1);
    $image->add_layer($text,-1);
    $text->set_name("Text");
    $shadow->plug_in_gauss_iir(10,1,1);
    $shadow->set_offsets(10,10);
    # On colorie le texte en rouge.
    $text->set_preserve_trans(1);
    $image->selection_all();
    Palette->set_foreground($col);
    $text->bucket_fill(FG_IMAGE_FILL,NORMAL_MODE,100,0,0,0,0);

# On réactive la commande Undo
    Gimp->gimp_undo_push_group_end($image); 

# On restaure les couleurs
    Palette->set_foreground($fg);
    Palette->set_background($bg);

# On renvoie l'image nouvellement créée (qui va s'afficher)
    return $image;
  };
exit main;

*

Arguments en ligne de commande

Il est possible de donner ces arguments en ligne de commande.

  perl 13.pl XYZ

Mais alors, l'image créée n'est plus affichée : c'est au script de prendre garde à la sauvegarder sur le disque.

Écrire un fichier sur le disque

Il est possible de sauvegarder les images au format XCF (propre à Gimp, permet de garder les calques et la transparence) ou en des formats plus courrants (PNG, JPEG), mais il faudra prendre garde à « applatir » l'image (ie, fusionner tous les calques visibles) avant de la sauvegarer, car on ne peut sauvegarder qu'un seul calque.

    my $flat = $image->flatten;
    $flat->file_jpeg_save("toto.jpg", "toto.jpg", .75, 1, 1, 1,
                          "Created with the Gimp",
                          0,1,0,0);

Lire un fichier

#! perl -w
use strict;

use Gimp;
use Gimp::Fu;
use Gimp::Util;

Gimp::set_trace(TRACE_ALL);

my $fontname = "-paradise-arnoldboecklin-extrabold-r-normal--0-0-0-0-p-0-iso8859-1";

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [
   [PF_STRING, "fichier", "fichier (JPEG) contenant l'image à modifier", "chateau.jpg"],
],
  sub {
    my ($f) = @_;
    my $image = Gimp->file_jpeg_load($f,$f);
    my $layer = $image->get_layers;

    $layer->desaturate;

    my $courbe = [];
    for(my $i=0; $i<256; $i++){
      push @$courbe, ( $i < 128  ) ?
                     (2*$i) :
                     (255-2*$i);
    }

    $layer->curves_explicit(VALUE_LUT, 3, $courbe);

    $layer->color_balance(SHADOWS,1, 25,-30,-65);

    Gimp->gimp_undo_push_group_start($image);
    my $fg = Palette->get_foreground();
    my $bg = Palette->get_background();

    Palette->set_foreground("black");
    Palette->set_background("white");

# On sauvegarde l'image
    my $flat = $image->flatten;
    $flat->file_jpeg_save("toto.jpg", "toto.jpg", .75, 1, 1, 1, 
                          "Created with the Gimp",
                          0,1,0,0);

# On réactive la commande Undo
    Gimp->gimp_undo_push_group_end($image); 

# On restaure les couleurs
    Palette->set_foreground($fg);
    Palette->set_background($bg);

# On renvoie l'image nouvellement créée (qui va s'afficher)
    return $image;
  };
exit main;

*

*

*

Texte en provenance de LaTeX

Si on veut mettre du texte un peu plus compliqué, on peut passer par LaTeX et inclure le fichier PostScript.

#! perl -w
use strict;

use Gimp;
use Gimp::Fu;
use Gimp::Util;

Gimp::set_trace(TRACE_ALL);

sub latex {
  my ($words) = @_;
  open(LATEX, ">tmp.tex") || die "Cannot open tmp.tex for writing: $!";
  print LATEX '\documentclass[12pt]{article}\usepackage[T1]{fontenc}\usepackage[latin1]{inputenc}' ."\n";
  print LATEX '\usepackage{amsfonts,amsmath}\usepackage[dvips,all]{xy}' ."\n";
  print LATEX '\pagestyle{empty}';
  print LATEX '\begin{document}';
  print LATEX $words;
  print LATEX '\end{document}';
  close LATEX;
  system 'latex', '--interaction=batchmode', 'tmp.tex';
  system qw/dvips -E -o tmp.eps tmp.dvi/;
}

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [
   [PF_STRING, "fichier", "fichier (JPEG) contenant l'image à modifier", "chateau.jpg"],
  ],
  sub {
    my ($f) = @_;
    my $image = Gimp->file_jpeg_load($f,$f);
    my $layer = $image->get_layers;
    # On éclaircit l'image de fond (pour pouvoir écrire dessus)
    $layer->levels(VALUE_LUT,
                   0, 255,    # In
                   1,         # Gamma correction 
                   150, 255); # Out
    # On calcule le texte que l'on va lui superposer
    latex('\huge$x^2 + y^2 = 1$');
    my $text_image = Gimp->file_ps_load("tmp.eps", "tmp.eps");
    my $text_layer = $text_image->get_layers;

    # On ajoute le texte à l'image
    $text_layer->edit_copy();
    my $text = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "LaTeX",
                  100, MULTIPLY_MODE);
    $image->add_layer($text, 0);
    $text->fill(TRANS_IMAGE_FILL);
    $text->edit_paste(0)->anchor;

    return $image;
  };
exit main;

*

Mais on n'a pas un très bon contrôle sur la taille de l'image : on peut convertir le fichier PS en fichier PPM soi même, en précisant la résolution.

sub latex {
  # Création du fichier PostScript
  my ($words) = @_;
  my $tmp = "tmp"; 
  open(LATEX, ">$tmp.tex") || die "Cannot open $tmp.tex for writing: $!";
  print LATEX '\documentclass[12pt]{article}\usepackage[T1]{fontenc}\usepackage[latin1]{inputenc}' ."\n";
  print LATEX '\usepackage{amsfonts,amsmath}\usepackage[dvips,all]{xy}' ."\n";
  print LATEX '\pagestyle{empty}';
  print LATEX '\begin{document}';
  print LATEX $words;
  print LATEX '\end{document}';
  close LATEX;
  system 'latex', '--interaction=batchmode', "$tmp.tex";
  system qw/dvips -E -D 600 -o tmp.eps/,  "$tmp.dvi";

  # Lecture de la Bounding Box
  my ($bbx,$bby,$bbw,$bbh);
  open(PS,"$tmp.eps");
  while (<PS>) {
    if (/^%%BoundingBox:\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)/) {
      $bbx = 0-$1;    $bby = 0-$2;
      $bbw = $3+$bbx;    $bbh = $4+$bby;
      last if /^%%EndComments/;
    }
  }
  close(PS);

  # Création du fichier *.ppm
  my $res = 4;
  $bbw *= $res;
  $bbh *= $res;
  my $r = $res*72;

  open(GS, "|gs -q -dNOPAUSE -dNO_PAUSE -sDEVICE=ppmraw -g${bbw}x${bbh} -r$r -sOutputFile=$tmp.ppm");
  print GS "$bbx $bby translate\n";
  print GS "($tmp.eps) run\n";
  print GS "showpage\n";
  print GS "quit\n";
  close(GS);

}
(...)
    latex('\huge\bfseries\LaTeX');
    my $text_image = Gimp->file_pnm_load("tmp.ppm", "tmp.ppm");
(...)

*

  sub {
    my ($f) = @_;
    my $image = Gimp->file_jpeg_load($f,$f);
    my $layer = $image->get_layers;

    # On calcule le texte que l'on va lui superposer
    latex('\huge\bfseries\LaTeX');
    my $text_image = Gimp->file_pnm_load("tmp.ppm", "tmp.ppm");
    my $text_layer = $text_image->get_layers;

    # On crée un nouveau calque
    my $text = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "LaTeX",
                  100, MULTIPLY_MODE);
    $image->add_layer($text, 0);
    $text->fill(TRANS_IMAGE_FILL);

    # On ajoute le texte à l'image
    $text_layer->edit_copy();
    # Si on ne précise pas d'offsets, l'image est centrée.
    # On la met en haut à gauche, à 40 pixels du bord
    my $border = 20;
    my $a = $text->edit_paste(0);
    $a->set_offsets(2*$border, $image->height - $text_image->height - 2*$border);
    $a->anchor;

    # On sélectionne un rectangle, 20 pixels autour de ce texte, dans l'image de fond
    $image->rect_select($border, $image->height - $text_image->height - 3*$border,
                        $text_image->width+2*$border, $text_image->height+2*$border,
                        REPLACE, 0, 0);
    # On éclaircit l'image de fond (pour pouvoir écrire dessus)
    $layer->levels(VALUE_LUT,
                   0, 255,    # In
                   1,         # Gamma correction 
                   150, 255); # Out

    my $flat = $image->flatten;
    $flat->file_jpeg_save("toto.jpg", "toto.jpg", .75, 1, 1, 1, 
                          "Created with the Gimp",
                          0,1,0,0);
    return $image;
  };

*

    $image->rect_select($border, $image->height - $text_image->height - 3*$border,
                        $text_image->width+2*$border, $text_image->height+2*$border,
                        REPLACE, 
                        1, $border, # feather
                       );

*

Masques

On peut vouloir utilise le texte LaTeX exactement comme du texte provenant de Gimp : en l'occurrence, avoir du texte noir sur un fond transparent. On peut le faire avec des masques.

    # Le fond
    my $background = $image->layer_new($image->width, $image->height,
                                  RGB_IMAGE,
                                  "Background",
                                  100, NORMAL_MODE);
    $image->add_layer($background, 0);
    $background->drawable_fill(BG_IMAGE_FILL);

    # Un nouveau calque, transparent
    my $layer = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "LaTeX",
                  100, NORMAL_MODE);
    $image->add_layer($layer, 0);
    $layer->fill(TRANS_IMAGE_FILL);

    # Calcul du texte
    latex('\huge\bfseries\LaTeX');
    my $text_image = Gimp->file_pnm_load("tmp.ppm", "tmp.ppm");
    my $text_layer = $text_image->get_layers;
    $text_layer->add_alpha;

    # On ajoute le texte à l'image
    $text_layer->edit_copy();
    $layer->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));

    # On crée un masque
    my $mask = $layer->create_mask(WHITE_MASK);
    $layer->add_layer_mask($mask);
    $mask->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));
    $mask->gimp_invert();
    $layer->remove_layer_mask(APPLY);

Transformation du blanc en transparent

    $layer->add_alpha;
    my $mask = $layer->create_mask(WHITE_MASK);
    $layer->add_layer_mask($mask);
    $layer->edit_copy();
    $mask->edit_paste(0)->anchor;
    $mask->gimp_invert();
    $layer->remove_layer_mask(APPLY);

Fontes : divers effets

Il est possible d'entrelacer des lettres (avec des masques), mais c'est plus facile à la souris qu'en Perl.

*

Lettres vieillies

    # $layer's background is white
    $layer->plug_in_spread(5,5);
    $layer->plug_in_spread(5,5);
    $layer->plug_in_gauss_iir(5,1,1);
    $layer->threshold(175,255);

*

*

Lettres transparentes

  sub {
    my ($f) = @_;
    my $image = Gimp->file_jpeg_load($f,$f);
    my $layer = $image->get_layers;

    my $fg = Palette->get_foreground();
    my $bg = Palette->get_background();
    Palette->set_foreground("black");
    Palette->set_background("white");

    latex('\huge\bfseries\LaTeX');
    my $text_image = Gimp->file_pnm_load("tmp.ppm", "tmp.ppm");
    my $text_layer = $text_image->get_layers;

    # On copie le fond (en ajoutant un channel alpha)
    my $copy = $layer->copy(1);
    $image->add_layer($copy,-1);
    # On l'éclaircit
    $copy->levels(VALUE_LUT,
                   0, 255,    # In
                   1,         # Gamma correction 
                   150, 255); # Out
    # On lui ajoute un masque
    my $mask = $copy->create_mask(WHITE_MASK);
    $copy->add_layer_mask($mask);
    # On met le texte dans ce masque
    $text_layer->edit_copy();
    $mask->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));
    $mask->gimp_invert();
    # On applique ce masque
    $layer->remove_layer_mask(APPLY);

    my $flat = $image->flatten;
    $flat->file_jpeg_save("toto.jpg", "toto.jpg", .75, 1, 1, 1, 
                          "Created with the Gimp",
                          0,1,0,0);
    return $image;
  };

*

On peut améliorer l'exemple précédent pour qu'il y ait un bord noir autour des lettres.

    # On rajoute le bord du texte
    my $bord = $image->layer_new($image->width, $image->height,
                  RGBA_IMAGE,
                  "contour",
                  100, MULTIPLY_MODE);
    $image->add_layer($bord, 0);
    $bord->fill(BG_IMAGE_FILL);
    $text_layer->edit_copy();
    $bord->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));
    #$bord->plug_in_sobel(1,1,1);
    $bord->plug_in_edge(3,1);
    $bord->gimp_invert();

*

Reprenons le même exemple, en transformant le blanc du calque du haut (celui contenant le bord des lettres) en transparent.

    my $m = $bord->create_mask(WHITE_MASK);
    $bord->add_layer_mask($m);
    $bord->edit_copy();
    $m->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));
    $m->gimp_invert();
    $bord->remove_layer_mask(APPLY);

Avec des images très en couleurs, on peut mettre les lettres en couleur sur un fond noir et blanc ou le contraire.

*

*

On peut aussi inverser le texte.

*

*

On peut aussi le solariser de deux manières différentes.

    # On solarise les deux calques de manière différente.
    my $courbe = [];
    for(my $i=0; $i<256; $i++){
      push @$courbe, ( $i < 128  ) ?
                     (2*$i) :
                     (255-2*$i);
    }
    $copy->curves_explicit(VALUE_LUT, 3, $courbe);
    $copy->color_balance(SHADOWS,1, 25,-30,-65);

    $courbe = [];
    for(my $i=0; $i<256; $i++){
      push @$courbe, ( $i < 128  ) ?
                     (255-2*$i) :
                     (2*$i-256);
    }
    $layer->curves_explicit(VALUE_LUT, 3, $courbe);
    #$layer->color_balance(SHADOWS,1, 25,-30,-65);

*

On peut récupérer l'une des images précédentes et la cabosser en suivant le texte.

    my $flat = $image->flatten;
    my $bump = $image->layer_new($image->width, $image->height,
                                 RGBA_IMAGE,
                                 "bumps",
                                 100, NORMAL_MODE);
    $image->add_layer($bump,0);
    $bump->fill(BG_IMAGE_FILL);
    $bump->edit_paste(0);
    gimp_floating_sel_anchor(gimp_image_floating_selection($image));
    $bump->desaturate();
    $flat->plug_in_bump_map($bump, 135, 45,
                                  10, # Profondeur, 
                                  0, 0, 0, 0,
                                  1, # Compensate for darkening
                                  0, LINEAR);
    $bump->remove_layer();

*

*

*

*

Installer un Plug-in

Considérons le programme suivant, appelé latex_string.pl, sui transforme du code LaTeX en une sélection flottante.

#! /share/nfs/users1/umr-tge/zoonek/gnu/Linux/bin/perl -w
use strict;

use Gimp qw(:auto N_);
use Gimp::Fu;
use Gimp::Util;

#Gimp::set_trace(TRACE_ALL);

register 
  "latex_string",
  "Convert LaTeX code into floating selection",
  "Convert LaTeX code into floating selection",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Image>/Filters/Render/LaTeX String",
  "",
  [
   [PF_STRING, "text", "LaTeX code to be compiled", "\\LaTeX"],
   [PF_STRING, "preamble", "Lines to add in the preamble", ""],
   [PF_INT32, "magnification", "TeX's \\magnification, around 4000 or 8000",4000],
  ],
  sub {
    my ($image, $drawable, $words, $preamble, $mag) = @_;

    # Création du fichier PostScript
    my $tmp = "tmp"; 
    open(LATEX, ">$tmp.tex") || die "Cannot open $tmp.tex for writing: $!";
    print LATEX '\documentclass[12pt]{article}\usepackage[T1]{fontenc}\usepackage[latin1]{inputenc}' ."\n";
    print LATEX '\usepackage{amsfonts,amsmath}\usepackage[dvips,all]{xy}' ."\n";
    print LATEX '\pagestyle{empty}';
    print LATEX "\n$preamble\n";
    print LATEX '\begin{document}';
    print LATEX $words;
    print LATEX '\end{document}';
    close LATEX;
    system 'latex', '--interaction=batchmode', "$tmp.tex";
    # -E      : EPSF
    # -D 600  : 600 dpi
    # -x 1000 : \magnification=1000
    system qw/dvips -x/, $mag, qw/-E -D 600 -o tmp.eps/,  "$tmp.dvi";
    
    # Lecture de la Bounding Box
    my ($bbx,$bby,$bbw,$bbh);
    open(PS,"$tmp.eps");
    while (<PS>) {
      if (/^%%BoundingBox:\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)/) {
	$bbx = 0-$1;    $bby = 0-$2;
	$bbw = $3+$bbx;    $bbh = $4+$bby;
	last if /^%%EndComments/;
      }
    }
    close(PS);

    # Création du fichier *.ppm
    my $res = 1;
    $bbw *= $res;
    $bbh *= $res;
    my $r = $res*72;
    
    open(GS, "|gs -q -dNOPAUSE -dNO_PAUSE -sDEVICE=ppmraw -g${bbw}x${bbh} -r$r -sOutputFile=$tmp.ppm");
    print GS "$bbx $bby translate\n";
    print GS "($tmp.eps) run\n";
    print GS "showpage\n";
    print GS "quit\n";
    close(GS);
    
    # Lecture du fichier *.ppm
    my $text_image = Gimp->file_pnm_load("$tmp.ppm","$tmp.ppm");
    my $text_layer = $text_image->get_layers;

    # On transforme le blanc en transparent
    $text_layer->add_alpha;
    my $text_mask = $text_layer->create_mask(WHITE_MASK);
    $text_layer->add_layer_mask($text_mask);
    $text_layer->edit_copy();
    $text_mask->edit_paste(0)->anchor;
    $text_mask->gimp_invert();
    $text_layer->remove_layer_mask(APPLY);

    # Copie du texte sur l'image
    $text_image->get_layers->edit_copy();
    $drawable->edit_paste(0);

    return $image;
  };
exit main;

On peut l'installer de la manière suivante.

gimptool --install-admin-bin latex_string.pl

Pour une installation personnelle :

gimptool --install-bin latex_string.pl

Animations

Pour créer une animation, il suffit de mettre plusieurs calques, chacun avec un nom se terminant par « (1000ms) » (avec les parnthèses) : l'image GIF ainsi créée affichera les calques un par un (en superposant chaque calque aux précédents), à raison d'un toutes les 100 milisecondes.

  for(...){
    $layer = $layer->copy(1);
    $layer->set_name("frame $i (1000ms)");
    $image->add_layer($layer,0);
    ...
  }
  ...
  $image->convert_indexed(1, WEB_PALETTE, 256, 1, 1, ""); 
  $layer->file_gif_save('toto.gif', 'toto.gif', 1, 1, 1000, 2);

*

Recoller deux images

#! perl -w

use strict;
use Gimp;

use Gimp::Fu;
use Gimp::Util;

#Gimp::set_trace(TRACE_ALL);

sub max ($$) {
  my ($a, $b) = @_;
  return $a > $b ? $a : $b;
}

register 
  "essai",
  "Premier essai",
  "Ceci est un premier essai de script en Perl pour Gimp",
  "Vincent Zoonekynd",
  "zoonek\@math.jussieu.fr",
  "0.1",
  "<Toolbox>/Xtns/Essais/1",
  "",
  [
   [PF_STRING, "fichier", "fichier (JPEG) contenant l'image de gauche"],
   [PF_STRING, "fichier", "fichier (JPEG) contenant l'image de droite"],
   [PF_STRING, "fichier", "fichier (JPEG) qui contiendra le résultat", "resultat
.jpg"],
  ],
  sub {
    my ($file_left, $file_right, $resultat) = @_;

    # Lecture des fichiers
    my $left = Gimp->file_jpeg_load($file_left, $file_left);
    my $left_layer = $left->get_layers;
    my $right = Gimp->file_jpeg_load($file_right, $file_right);
    my $right_layer = $right->get_layers;

    # Création d'une nouvelle image
    my $image = new Image( $left->width + $right->width,
                           max($left->height, $right->height),
                           RGB);
    my $background = $image->layer_new($image->width, $image->height,
                                       RGB_IMAGE,
                                       "Background",
                                       100, NORMAL_MODE);
    $image->add_layer($background, 0);
    $background->drawable_fill(BG_IMAGE_FILL);

    # Copie des deux fichiers dans la nouvelle image
    # (si on ne précise pas d'offset, l'image des centrée)
    $left_layer->edit_copy();
    my $a = $background->edit_paste(0);
    $a->set_offsets(0, 0);
    $a->anchor;

    $right_layer->edit_copy();
    my $b = $background->edit_paste(0);
    $b->set_offsets($left->width, 0);
    $b->anchor;

    # Sauvegarde du fichier
    my $flat = $image->flatten;
    $flat->file_jpeg_save($resultat, $resultat, .75, 1, 1, 1,
                          "Created with the Gimp",
                          0,1,0,0);
    };

# Ne pas oublier cette dernière ligne...
exit main;

PDL

...

Au prompt du shell, taper
  perldl
puis
  demo
  demo pdl
  demo 3d
  demo 3d2
  demo 3dgal

perldoc Gimp::PDL

         use Gimp;
         use Gimp::PDL;
         use PDL;

        $region = $drawable->get->pixel_rgn (0,0, 100,100, 1,0);
        $pixel = $region->get_pixel (5,7);     # fetches the pixel from (5|7)
        print $pixel;                          # outputs something like
                                               # [255, 127, 0], i.e. in
                                               # RGB format ;)
        $region->set_pixel ($pixel * 0.5, 5, 7);# darken the pixel
        $rect = $region->get_rect (3,3,70,20); # get a horizontal stripe
        $rect = $rect->hclip(255/5)*5;         # clip and multiply by 5
        $region->set_rect($rect);              # and draw it!
        undef $region;                         # and update it!

...

Vincent Zoonekynd
<zoonek@math.jussieu.fr>
Octobre 2000
latest modification on ven mar 22 15:43:46 CET 2002