On a besoin de leurs auteurs! :). Participez à notre didacticiel.
Si vous devez utiliser un de ces widgets non documentés, je vous recommande fortement de consulter leurs fichiers en-têtes respectifs dans la distribution GTK. Les noms de fonctions du GTK sont très parlantes. Lorsque vous avez compris comment les choses fonctionnent, il n'est pas difficile de savoir comment utiliser un widget à partir des déclarations de ses fonctions. Cela, avec quelques exemples de codes pris ailleurs, devrait ne pas poser de problème.
Lorsque vous avez compris toutes les fonctions d'un nouveau widget non documenté, pensez à écrire un didacticiel pour que les autres puissent bénéficier du temps que vous y avez passé.
(Ceci peut devoir être réécrit pour suivre le style du reste de ce didacticiel).
Les prévisualisateurs servent à plusieurs choses dans GIMP/GTK. La
plus importante est celle-ci : les images de haute qualité peuvent
occuper des dizaines de mega-octets en mémoire - facilement ! Toute
opération sur une image aussi grosse implique un temps de traitement
élevé. Si cela vous prend 5 à 10 essais (i.e. 10 à 20 étapes puisque
vous devez recommencer lorsque vous avez fait une erreur) pour choisir
la bonne modification, cela prendra littéralement des heures pour
produire la bonne image - pour peu que vous ne manquiez pas de mémoire
avant. Ceux qui on passé des heures dans les chambres noires de
développement couleur connaissent cette sensation. Les
prévisualisations sont notre planche de salut !
L'aspect pénible de l'attente n'est pas le seul problème. souvent, il
est utile de comparer les versions « Avant » et « Après » côte à côte
ou, au pire l'une après l'autre. Si vous travaillez avec de grosses
images et des attentes de 10 secondes, l'obtention des versions «
Avant » et « Après » est, pour le moins, difficile. Pour des images de
30Mo (4"x6", 600dpi, 24 bits), la comparaison côte à côte est
impossible pour la plupart des gens, et la comparaison séquentielle
n'est guère mieux. Les prévisualisations sont notre planche de salut !
Mais il y a plus. Les prévisualisations permettent les
pré-prévisualisations côte à côte. En d'autres termes, vous écrivez un
plug-in (par exemple la simulation filterpack) qui aurait plusieurs
prévisualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche
comme celle ci agit comme une sorte de palette de prévisualisation et
est très pratique pour les petites modifications. Utilisons les
prévisualisations !
Encore plus : pour certains plug-ins une intervention humaine en
temps réel, spécifique aux images, peut s'avérer nécessaire. Dans le
plug-in SuperNova, par exemple, on demande à l'utilisateur d'entrer
les coordonnées du centre de la future supernova. La façon la plus
simple de faire cela, vraiment, est de présenter une prévisualisation
à l'utilisateur et de lui demander de choisir interactivement le
point. Utilisons les prévisualisations !
Enfin, quelques utilisations diverses : on peut utiliser les
prévisualisations, même lorsqu'on ne travaille pas avec de grosses
images. Elles sont utiles, par exemple, lorsqu'on veut avoir un rendu
de motifs complexes. (Testez le vénérable plug-in Diffraction et
d'autres !). Comme autre exemple, regardez le plug-in de rotation de
couleur (travail en cours). Vous pouvez aussi utiliser les
prévisualisations pour des petits logos dans vos plug-ins et même pour
une photo de vous, l'Auteur. Utilisons les prévisualisations !
Quand ne pas utiliser les prévisualisations
N'utilisez pas les prévisualisations pour les graphes, les tracés,
etc. GDK est bien plus rapide pour ça. N'utilisez les que pour les
images !
Utilisons les prévisualisations !
Vous pouvez mettre une prévisualisation dans à peu près n'importe
quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
meilleur d'elles-mêmes dans des cadres resserrés autour d'elles. Les
prévisualisations n'ont, par elles-mêmes, aucun contour et semblent
plates sans eux. (Bien sûr, si c'est cet aspect que vous
voulez...). Les cadres serrés fournissent les bordures nécessaires.
[Image][Image]
Les prévisualisations sont, à bien des égards, comme tous les autres
widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent
d'une fonctionnalité supplémentaire : ils doivent être remplis
avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK
des prévisualisations, puis nous verrons comment les remplir.
/* Création d'un widget prévisualisation,
* configuration de sa taille et affichage */
GtkWidget *preview;
preview=gtk_preview_new(GTK_PREVIEW_COLOR)
/* Autre option :
GTK_PREVIEW_GRAYSCALE);*/
gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
gtk_widget_show(preview);
my_preview_rendering_function(preview);
Ah oui, comme je le disais, les prévisualisations rendent mieux dans
des cadres :
GtkWidget *create_a_preview(int Width,
int Height,
int Colorfulness)
{
GtkWidget *preview;
GtkWidget *frame;
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_border_width (GTK_CONTAINER(frame),0);
gtk_widget_show(frame);
preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
:GTK_PREVIEW_GRAYSCALE);
gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
gtk_container_add(GTK_CONTAINER(frame),preview);
gtk_widget_show(preview);
my_preview_rendering_function(preview);
return frame;
}
Ceci est ma prévisualisation de base. Cette fonction retourne le cadre
« père », on peut ainsi le placer ailleurs dans notre interface. Bien
sûr, on peut passer le cadre « père » en paramètre à cette
fonction. Dans de nombreuses situations, toutefois, le contenu de la
prévisualisation est changée continuellement par notre application. En
ce cas, on peut passer un pointeur vers une prévisualisation à la
fonction <em/create_a_preview()/ et avoir ainsi un contrôle sur elle
plus tard.
Un point plus important qui pourra un jour vous faire économiser
beaucoup de temps. Quelques fois, il est souhaitable de mettre un
label à votre prévisualisation. Par exemple, on peut nommer la
prévisualisation contenant l'image originale « Original » et celle
contenant l'image modifiée « Moins Originale ». Il peut vous arriver
de placer la prévisualisation avec le label approprié dans une
vbox. L'effet inattendu est que si le label est plus large que la
prévisualisation (taille de cette dernière, taille de la fonte du
label, etc), le cadre s'élargit et ne convient plus à la
prévisualisation. Le même problème se passera probablement dans
d'autres situations aussi.
[Image]
La solution consiste à placer la prévisualisation et le label dans une
table de 2x2 en les attachant avec les paramètres suivants (c'est
l'une des possibilités, bien sûr. La clé consiste à ne pas mettre
GTK_FILL dans le second attachement)«nbsp;:
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
0,
GTK_EXPAND|GTK_FILL,
0,0);
gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
GTK_EXPAND,
GTK_EXPAND,
0,0);
Et voici le résultat :
[Image]
Divers
Rendre une prévisualisation cliquable se fait très facilement en la plaçant dans un bouton. Cela ajoute aussi une bordure agréable autour de la prévisualisation et vous n'avez même pas besoin de la mettre dans un cadre. Voir le plug-in Filter Pack Simulation comme exemple.
Remplir une prévisualisation
Afin de nous familiariser avec les bases de ce remplissage, créons le motif suivant :
[Image]
void
my_preview_rendering_function(GtkWidget *preview)
{
#define SIZE 100
#define HALF (SIZE/2)
guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits par point */
gint i, j; /* Coordonnées */
double r, alpha, x, y;
if (preview==NULL) return; /* J'ajoute généralement ceci quand je */
/* veux éviter des plantages stupides */
/* Vous devez vous assurer que tout a */
/* été correctement initialisé ! */
for (j=0; j < ABS(cos(2*alpha)) ) { /* Sommes-nous dans la forme ? */
/* glib.h contient ABS(x). */
row[i*3+0] = sqrt(1-r)*255; /* Definit rouge */
row[i*3+1] = 128; /* Definit vert */
row[i*3+2] = 224; /* Definit bleu */
} /* "+0" est pour l'alignement ! */
else {
row[i*3+0] = r*255;
row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
}
}
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
/* Insère "row" dans "preview" en partant du point de */
/* coordonnées (0,j) première colonne, j_ième ligne allant de SIZE */
/* pixels vers la droite */
}
free(row); /* on récupère un peu d'espace */
gtk_widget_draw(preview,NULL); /* qu'est-ce que ça fait ? */
gdk_flush(); /* et ça ? */
}
Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
déjà beaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
précisions à ajouter.
Prévisualisation d'image
Il est pratique de conserver une version réduite de l'image ayant
juste assez de pixels pour remplir la prévisualisation. Ceci est
possible en choisissant chaque énième pixel où n est le ratio de la
taille de l'image par rapport à la taille de la visualisation. Toutes
les opérations suivantes (y compris le remplissage des
prévisualisations) sont alors réalisées seulement sur le nombre réduit
de pixels. Ce qui suit est mon implantation de la réduction d'image
(Gardez à l'esprit que je n'ai que quelques notions de base en C !).
(ATTENTION : CODE NON TESTÉ !!!)
typedef struct {
gint width;
gint height;
gint bbp;
guchar *rgb;
guchar *mask;
} ReducedImage;
enum {
SELECTION_ONLY,
SELCTION_IN_CONTEXT,
ENTIRE_IMAGE
};
ReducedImage *Reduce_The_Image(GDrawable *drawable,
GDrawable *mask,
gint LongerSize,
gint Selection)
{
/* Cette fonction réduit l'image à la taille de prévisualisation choisie */
/* La taille de la prévisualisation est déterminée par LongerSize, i.e. */
/* la plus grande des deux dimensions. Ne fonctionne qu'avec des images */
/* RGB ! */
gint RH, RW; /* Hauteur et Largeur réduites */
gint width, height; /* Largeur et Hauteur de la surface à réduire */
gint bytes=drawable->bpp;
ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
gint i, j, whichcol, whichrow, x1, x2, y1, y2;
GPixelRgn srcPR, srcMask;
gint NoSelectionMade=TRUE; /* Suppose que l'on traite l'image entière */
gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
width = x2-x1;
height = y2-y1;
/* S'il y a une SELECTION, on récupère ses frontières ! */
if (width != drawable->width && height != drawable->height)
NoSelectionMade=FALSE;
/* On vérifie si l'utilisateur a rendu une sélection active */
/* Ceci sera important plus tard, lorsqu'on créera un masque réduit */
/* Si on veut prévisualiser l'image entière, supprimer ce qui suit ! */
/* Bien sûr, s'il n'y a pas de sélection, cela n'a aucun effet ! */
if (Selection==ENTIRE_IMAGE) {
x1=0;
x2=drawable->width;
y1=0;
y2=drawable->height;
}
/* Si on veut prévisualiser une sélection avec une surface qui l'entoure, */
/* on doit l'agrandir un petit peu. Considérez ça comme une devinette. */
if (Selection==SELECTION_IN_CONTEXT) {
x1=MAX(0, x1-width/2.0);
x2=MIN(drawable->width, x2+width/2.0);
y1=MAX(0, y1-height/2.0);
y2=MIN(drawable->height, y2+height/2.0);
}
/* Calcul de la largeur et de la hauteur de la surface à réduire. */
width = x2-x1;
height = y2-y1;
/* Les lignes ci-dessous déterminent la dimension qui sera le coté */
/* le plus long. Cette idée est empruntée au plug-in Supernova. */
/* Je soupçonne que j'aurais pu y penser moi-même, mais la vérité */
/* doit être dite. Le plagiat pue ! */
if (width>height) {
RW=LongerSize;
RH=(float) height * (float) LongerSize/ (float) width;
}
else {
RH=LongerSize;
RW=(float)width * (float) LongerSize/ (float) height;
}
/* L'image entière est réduite dans une chaîne ! */
tempRGB = (guchar *) malloc(RW*RH*bytes);
tempmask = (guchar *) malloc(RW*RH);
gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE);
/* Réservation pour sauver une ligne d'image et une ligne du masque */
src_row = (guchar *) malloc (width*bytes);
src_mask_row = (guchar *) malloc (width);
for (i=0; i < RH; i++) {
whichrow=(float)i*(float)height/(float)RH;
gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width);
gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width);
for (j=0; j < RW; j++) {
whichcol=(float)j*(float)width/(float)RW;
/* Pas de sélection = chaque point est complètement sélectionné ! */
if (NoSelectionMade)
tempmask[i*RW+j]=255;
else
tempmask[i*RW+j]=src_mask_row[whichcol];
/* Ajout de la ligne à la longue chaîne qui contient maintenant */
/* l'image ! */
tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
/* On s'accroche aussi à l'alpha */
if (bytes==4)
tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
}
}
temp->bpp=bytes;
temp->width=RW;
temp->height=RH;
temp->rgb=tempRGB;
temp->mask=tempmask;
return temp;
}
La suite est une fonction de prévisualisation qui utilise le même type
<em/ReducedImage/ ! On remarque qu'elle utilise une fausse
transparence (au moyen de <em/fake_transparancy/ qui est défini comme
suit :
gint fake_transparency(gint i, gint j)
{
if ( ((i%20)- 10) * ((j%20)- 10)>0 )
return 64;
else
return 196;
}
Voici maintenant la fonction de prévisualisation«nbsp;:
void
my_preview_render_function(GtkWidget *preview,
gint changewhat,
gint changewhich)
{
gint Inten, bytes=drawable->bpp;
gint i, j, k;
float partial;
gint RW=reduced->width;
gint RH=reduced->height;
guchar *row=malloc(bytes*RW);;
for (i=0; i < RH; i++) {
for (j=0; j < RW; j++) {
row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
if (bytes==4)
for (k=0; k<3; k++) {
float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
}
}
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
}
free(a);
gtk_widget_draw(preview,NULL);
gdk_flush();
}
Fonctions applicables
guint gtk_preview_get_type (void);
/* Aucune idée */
void gtk_preview_uninit (void);
/* Aucune idée */
GtkWidget* gtk_preview_new (GtkPreviewType type);
/* Décrite ci-dessous */
void gtk_preview_size (GtkPreview *preview,
gint width,
gint height);
/* Permet de changer la taille d'une prévisualisation existante */
/* Apparamment, il y a un bug dans GTK qui rend ce traitement */
/* hasardeux. Une méthode pour corriger ce problème consiste à */
/* changer manuellement la taille de la fenêtre contenant la */
/* prévisualisation après avoir changé la taille de la */
/* prévisualisation. */
void gtk_preview_put (GtkPreview *preview,
GdkWindow *window,
GdkGC *gc,
gint srcx,
gint srcy,
gint destx,
gint desty,
gint width,
gint height);
/* Aucune idée */
void gtk_preview_put_row (GtkPreview *preview,
guchar *src,
guchar *dest,
gint x,
gint y,
gint w);
/* Aucune idée */
void gtk_preview_draw_row (GtkPreview *preview,
guchar *data,
gint x,
gint y,
gint w);
/* Décrite dans le texte */
void gtk_preview_set_expand (GtkPreview *preview,
gint expand);
/* Aucune idée */
/* Aucune piste pour celles qui suivent mais devrait être */
/* un standard pour la plupart des widgets. */
void gtk_preview_set_gamma (double gamma);
void gtk_preview_set_color_cube (guint nred_shades,
guint ngreen_shades,
guint nblue_shades,
guint ngray_shades);
void gtk_preview_set_install_cmap (gint install_cmap);
void gtk_preview_set_reserved (gint nreserved);
GdkVisual* gtk_preview_get_visual (void);
GdkColormap* gtk_preview_get_cmap (void);
GtkPreviewInfo* gtk_preview_get_info (void);
That's all, folks!