STATUT : COMPLET

Téléchargement du tutoriel complet : tutoriel 3
Décompressez l'archive et copiez le dossier "3" dans "%scol%/partition/tutoriels/codes/" où %scol% est le dossier d'installation de Scol. Ok pour toute version de Scol supérieure à la 4.2.


Objectif : Affichage de bitmaps transformés dans une fenêtre.

  • fonctions courantes sur les bitmaps
  • rafraichissement et création de bitmaps
  • gestion du temps (timer)
  • génération de nombre aléatoire
  • prototypage (typage explicite de fonctions)

Ne pas oublier de consulter la doc de référence (liste des fonctions Scol au format html) pour toutes les fonctions Scol croisées dans ce package et dans les suivants ! Cette doc devrait être toujours à portée de clic lorsque vous codez en Scol ;-)

Une partie de la doc de l'API multimedia n'existe que dans la 3.5 par exemple. N'hésitez pas à demander en cas de doute.


Ce package Scol est chargé grâce au script "launch.scol" présent dans le même dossier. La fonction 'main', située en fin de fichier, est la fonction primaire appelée par ce script.

Cette application créera un bitmap de la taille de la fenêtre et l'affichera. Des points aléatoires se déposeront dessus à intervalle régulier. Il convient donc de créer ce bitmap, de gérer son affichage et d'intégrer un timer pour ajouter les pointillés.


- BITMAPS

Les bitmaps (de type ObjBitmap) sont manipulables et transformables en Scol. Non seulement on peut les créer directement à partir de fichiers graphiques BMP, JPEG ou TARGA ou indirectement à partir de fichiers PNG mais on peut aussi les créer de toutes pièces dans Scol, sans aucun support "physique".

Dans ce tutoriel, nous allons voir comment les créer en Scol et comment les transformer en leur application quelques modificateurs parmi les plus couramment utilisés (mais il en existe plein d'autres, voir la doc de référence mentionnée ci-dessus).

- TIMERS

Les objets de type Timer permettent, comme leur nom l'indique, d'avoir une base de temps régulière. Plus précisément, ils permettent d'obtenir des "top" réguliers afin d'exécuter des instructions périodiquement.

- PROTOTYPES

Le prototypage d'une fonction est la déclaration explicite du type d'une fonction (de ses arguments éventuels et de sa valeur retournée).
Normalement, le compilateur se débrouille tout seul pour déterminer les types des fonctions d'une application. Il le fait très bien dans 99,99% des cas. Dans les 0,01% restants, on trouve deux cas de figures :

  • le typage reste ambigü, et le compilateur aime les certitudes.
  • on est contraint de déclarer une fonction après son utilisation (c'est le cas dans cette exemple).

Le typage ambigü est généralement une fonction qui retourne 'nil' tout le temps. Dans ce cas, il suffit de retourner 0 (ou tout valeur d'un type non ambigü) dans au moins un cas et c'est réglé.

Le prototypage consiste simplement à rendre explicite le typage d'une fonction, donc à imposer un type précis de la fonction au compilateur. Bien sur, ce dernier verifiera que le prototypage est cohérent avec la fonction et génèrera une erreur le cas échéant. Une fonction ainsi prototypée pourra donc être utilisée avant d'être déclarée si c'est deux conditions sont remplies :

  • que la définition de la fonction soit compilée avant l'exécution : la fonction pourra être déclarée dans un package chargé et compilé après celui où elle est utilisée, par exemple mais avant que l'application s'exécute.
  • que la définition de cette fonction existe ! Prototyper une fonction qui n'existe dans aucun package chargé et compilé provoquera une erreur ("prototype unknown" ou "prototype empty") à l'exécution.

La syntaxe est la suivante : mot clé "proto" suivi du nom de la fonction, du signe "=" et de son typage et terminer par ";;"

'proto mafonctionprototypee = fun [S [S I r1]] [[I I] r1];;'

On ne peut pas prototyper de la sorte une variable (quel en serait l'intérêt ?).

Dans cet exemple, on doit prototyper la fonction 'createBitmap'. Pourquoi ?
'createBitmap' permet de créer les bitmaps nécessaires à notre application : on les crée au lancement, c'est normal. Mais on doit aussi l'utiliser lorsque l'utilisateur redimmensionne la fenêtre de l'application.
Bien sur, j'aurais pu définir cette fonction en amont de la fonction 'resize' et le problème aurait semblé résolu. En fait non, car 'resize' est une callback définie dans la fonction 'createInterface', elle-même appelée par 'createBitmap' ... Or, 'createBitmap' est aussi appelée par 'resize', donc ...!


// - DÉCLARTATIONS

// fenêtre mère de l'objet
typeof win = ObjWin;;	
// timer pour obtenir un 'top' régulier
typeof timer = Timer;;	

// prototypage de la fonction 'createBitmap' (cf ci-dessus)
proto createBitmap = fun [I I] I;;	

// - FONCTIONS

Callbacks générales de l'application

// Destruction de l'application
fun end(win, bmp)=
	_DSbitmap bmp;
	_deltimer timer;
	_closemachine;;
	
// Lorsque la fenêtre est redimensionnée, on recrée un bitmap à la nouvelle taile
fun resize(win, u, largeur, hauteur)=
	createBitmap largeur hauteur;;

// A chaque rafraichissement normal ou forcée, le bitmap est affichée
fun paint(win, bmp)=
	_BLTbitmap win bmp 0 0;;
	
// Lorsqu'on clique dans la fenêtre, le bitmap est passé en négatif. On n'oublie pas de réévaluer 
// la callback de paint et d'afficher le négatif du bitmap courant
fun click(win, bmp, xclick, yclick, boutonsouris)=
	let _GETbitmapSize bmp -> [w h] in
	let _NOTbitmap bmp 0 0 w h -> bmp in
	(
	 _CBwinPaint win @paint bmp;
	 paint win bmp
	);;

Callback de l'objet Timer de l'application, on y crée les petits points de couleur aléatoire et qui viendront se placer aléatoirement sur le bitmap.
La génération des nombres aléatoires se fait via la fonction Scol 'rand' après l'avoir initialisé avec 'srand' (voir plus bas).
Les manips sur les bitmaps sont du même ordre que celles décrites dans les commentaires de 'createBitmap' ci-après.

Son type est fun [Timer ObjBitmap] I

fun cbTimer(t, bmp)=
	let _FILLbitmap _CRbitmap _channel 6 6 0 -> tmp in
	let _FILLbitmap8 _CRbitmap8 _channel 6 6 0xFFFFFF -> tmp8 in
	let _DRAWcircle tmp 3 3 3 DRAW_INVISIBLE 0 0 DRAW_SOLID mod rand 0xFFFFFF -> _ in
	let _CRalphaBitmap _channel tmp tmp8 nil 0 -> abmp in
	let _GETbitmapSize bmp -> [w h] in
	let mod rand w -> x in
	let mod rand h -> y in
	let _CPalphaBitmap bmp x y abmp 0 0 6 6 -> _ in
	(
	 _DSalphaBitmap abmp;
	 _DSbitmap8 tmp8;
	 _DSbitmap tmp;
	 paint win bmp;
	 0
	);;

'createInterface' crée l'interface graphique de l'utilisateur si celle-ci n'existe pas déjà (cf problématique du prototypage en tête de fichier) puis définie un certain nombre de callback susceptible d'évoluer à chaque modification du bitmap central.

En effet, si on ne réévaluer pas ces callbacks, elles garderaient en paramètre le bitmap initial et non celui mis à jour. Pensez-y à l'avenir !!

Un objet de type Timer est une sorte de chronomètre : on lui indique une période et il va "bipper" à chaque période. Ainsi, la callback qui lui est associée ('_rfltimer') sera appellé périodiquement. Les fonctions Scol des Timer sont :

  • '_starttimer' : canal, période : crée un Timer
  • '_deltimer' : Timer : détruit un Timer
  • '_rlftimer' : Timer fun [u0] u1 u0 : callback du Timer
  • '_settimerperiod' : Timer période : définit une nouvelle période pour le Timer (à partir de Scol 4.6)
  • '_gettimerscapabilities' : retourne les périodes min et max supportées par le client (à partir de Scol 4.6).
Les périodes sont en millisecondes (ms).

La dernière instructions de la fonction permet d'afficher de suite (= sans attendre un éventuel raffraichissement de la fenêtre) le bitmap dans la fenêtre.

Son type est fun [ObjBitmpa I I] I

fun createInterface(bmp, w, h)=
	if win == nil then
		(
		 set win = _CRwindow _channel nil 0 0 w h WN_NORMAL " tutoriel 3";
		 set timer = _starttimer _channel 100;
		 _CBwinSize win @resize 0;
		)
	else
		nil;
	_rfltimer timer @cbTimer bmp;
	_CBwinDestroy win @end bmp;
	_CBwinClick win @click bmp;
	_CBwinPaint win @paint bmp;
	paint win bmp;
	0;;

'createMask' crée des AlphaBitmaps afin d'avoir des masques pour gérer des transparences notamment.

On crée ainsi un AlphaBitmap sans support physique (fonction '_CRalphaBitmap') : un bitmap apporté par le premier argument de la fonction et un bitmap codé sur 8 bits qui est créé juste avant et qui servira de "couche alpha". Pour cela, on lui appliquera une couleur (définie par le second argument de la fonction) unie ('_FILLbitmap8').

Le type de 'createMask' est fun [ObjBitmap I] AlphaBitmap

fun createMask(bmp, color)=
	let _GETbitmapSize bmp -> [w h] in
	let _FILLbitmap8 _CRbitmap8 _channel w h color -> bmp8 in
	let _CRalphaBitmap _channel bmp bmp8 nil nil -> abmp in
	let _DSbitmap8 bmp8 -> _ in
	abmp;;

La fonction 'createBitmap' crée un bitmap d'une taille définie par les deux arguments (largeur, 'w' pour width et hauteur, 'h' pour height).

On a vu dans le tutoriel précédent le chargement d'un bitmap depuis un fichier du disque dur (fonction Scol '_LDbitmap', '_LDjpeg' ou encore '_LDalphaBitmap' ...). Ici, on crée un bitmap de toutes pièces, à partir d'aucun support physique.

Pour créer notre bitmap, on va utiliser différentes fonctions. Entre autres, l'utilisation de masques "alpha" permettra quelques effets de transparence. Cette fonction sera appellée plusieurs fois, selon les actions de l'utilisateur.

  1. '_CRbitmap' est une fonction Scol qui crée donc un tel bitmap. Elle prend en argument la hauteur et la largeur désirées. '_channel' indique que ce bitmap sera créé dans le canal courant. Les canaux seront expliqués au tutoriel n°8 "Création d'une application client - serveur". Pour l'instant, on travaillera toujours dans le canal courant. On verra donc '_channel' qui est la fonction Scol qui retourne ce canal courant.
  2. '_DRAWgradient' ajoute un dégradé de couleur à un bitmap (créé par _CRbitmap ou chargé depuis un fichier du disque dur, peu importe).
  3. '_DRAWrectangle' ajoute un rectangle coloré à un bitmap (avec ou sans bordure). Les flags 'DRAW_SOLID' et 'DRAW_INVISIBLE' indiquent respectivement si l'option concernée est activée ou pas.
  4. '_DRAWline' ajoute une ligne droite à un bitmap.
  5. '_BLURbitmap' floute le contenu d'un bitmap.
  6. 'make_rgb' retourne la valeur de la couleur en 24 bits suivant les valeurs de ses composantes Rouge, Verte et Bleue en 8 bits.
  7. '_GETstringSize' calcule, d'après la police (fonte) utilisée, la largeur et la hauteur occupée par un texte (dans un bitmap ou pas).
  8. '_DRAWtext' intègre un texte dans un ObjBitmap.
  9. '_CRfont' crée un ObjFont : un objet police de caractère reconnu par Scol (ça ne crée pas une police en tant que telle !). La police doit exister sur le système qui lit l'application ... !!
  10. '_CPalphaBitmap' copie le contenu d'un AlphaBitmap dans un ObjBitmap.
  11. 'createMask' est une fonction de cette application, naturellement définie avant son utilisation. Voir donc son commentaire ci-dessus.

Enfin, une fois l'ObjBitmap créé, on lance l'interface graphique.

Son type est fun [I I] I

fun createBitmap(w, h)=
	let _CRbitmap _channel w h -> bmp in
	let _CRfont _channel 36 0 0 "Arial" -> font in
	let _GETstringSize font "Scol" -> [ws hs] in
	
	let createMask _DRAWgradient 
						_CRbitmap _channel w/2 h/2 
						0 0 w/2 w/2 0x222222 0xDDDDDD 270
					0x777777
		-> abmp in
	let createMask _DRAWgradient 
						_CRbitmap _channel w/2 h/2 
						0 0 w/2 w/2 0x222222 0xDDDDDD 270
					0x333333
		-> abmp2 in
	let createMask _DRAWgradient 
						_CRbitmap _channel w/2 h/2 
						0 0 w/2 w/2 0x222222 0xDDDDDD 270
					0x111111
		-> abmp3 in
	(
	 _DRAWrectangle bmp 0 0 w h DRAW_SOLID 5 0 DRAW_SOLID make_rgb 20 55 20;
	 _CPalphaBitmap bmp w/4+10 h/4+10 abmp 0 0 w/2 h/2;
	 _CPalphaBitmap bmp w/4+20 h/4+20 abmp2 0 0 w/2 h/2;
	 _CPalphaBitmap bmp w/4+30 h/4+30 abmp3 0 0 w/2 h/2;
	 _DRAWgradient bmp w/4 h/4 w/2 h/2 0x222222 0xDDDDDD 270;
	 _DRAWtext bmp font (w/2)-(ws/2) (h/2)+(hs/2) TD_LEFT|TD_BASELINE make_rgb 20 55 20 "Scol";
	 _DRAWline bmp (w/2)-(ws/2)-20 (h/2)+hs (w/2)+(ws/2)+20 (h/2)+hs DRAW_SOLID 2 make_rgb 20 55 20;
	 _BLURbitmap bmp (w/2)-(ws/2) (h/2)-10 ws hs 8 0;
	 
	 _DSalphaBitmap abmp;
	 _DSalphaBitmap abmp2;
	 _DSalphaBitmap abmp3;
	 
	 createInterface bmp w h
	);;

Première fonction appellée.

Elle lance la fonction de création d'un bitmap après avoir initialisé la génération de variable aléatoire (fonction Scol 'srand')
L'appel de la fonction 'createBitmap' se fait avec deux arguments de type I (integer) puisque cela est demandé par la fonction (cf ci-dessus).

Le type de la fonction 'main' est fun [] I

fun main()=
	srand _tickcount;
	createBitmap 800 600;;