20131007:TP:OCaml:SDL
Sommaire
Utiliser SDL
- Cette partie du TP est à rendre via l'interface de rendu en suivant ce lien [1]. Le suffixe de rendu est tp20131007: votre répertoire devra donc s'appeler login-tp20131007 et votre archive login-tp20131007.tar.bz2
- Fichiers à rendre:
- AUTHORS
- Makefile
- _tags
- tpsdl.ml
Nous utiliserons le Makefile suivant:
# TP sdl OCAML=ocamlopt OCAMLFLAGS= -I +sdl -I +site-lib/sdl OCAMLLD= bigarray.cmxa sdl.cmxa sdlloader.cmxa tpsdl: tpsdl.ml ${OCAML} ${OCAMLFLAGS} ${OCAMLLD} -o tpsdl tpsdl.ml clean:: rm -f *~ *.o *.cm? tpsdl # FIN
Et le fichier _tags pour ocamlbuild suivant:
<*.{byte,native}>: package(sdl), package(sdl.sdlimage) <*.ml>: package(sdl), package(sdl.sdlimage) <*.mli>: package(sdl), package(sdl.sdlimage)
Le fichier AUTHORS devra avoir la forme suivante:
* login (Prenom Nom)
Code fourni
Pour la suite, nous vous fournissons quelques éléments de code. Pour les détails référez vous à la documentation d'OCamlSDL [2]:
(* Dimensions d'une image *) let get_dims img = ((Sdlvideo.surface_info img).Sdlvideo.w, (Sdlvideo.surface_info img).Sdlvideo.h) (* init de SDL *) let sdl_init () = begin Sdl.init [`EVERYTHING]; Sdlevent.enable_events Sdlevent.all_events_mask; end (* attendre une touche ... *) let rec wait_key () = let e = Sdlevent.wait_event () in match e with Sdlevent.KEYDOWN _ -> () | _ -> wait_key () (* show img dst affiche la surface img sur la surface de destination dst (normalement l'écran) *) let show img dst = let d = Sdlvideo.display_format img in Sdlvideo.blit_surface d dst (); Sdlvideo.flip dst (* main *) let main () = begin (* Nous voulons 1 argument *) if Array.length (Sys.argv) < 2 then failwith "Il manque le nom du fichier!"; (* Initialisation de SDL *) sdl_init (); (* Chargement d'une image *) let img = Sdlloader.load_image Sys.argv.(1) in (* On récupère les dimensions *) let (w,h) = get_dims img in (* On crée la surface d'affichage en doublebuffering *) let display = Sdlvideo.set_video_mode w h [`DOUBLEBUF] in (* on affiche l'image *) show img display; (* on attend une touche *) wait_key (); (* on quitte *) exit 0 end let _ = main ()
Compilation
Pour la compilation, il y a plusieurs méthodes:
- à la main
- avec make
- avec ocamlbuild
Je vous laisse essayer de comprendre à partir du fichier Makefile comment le faire à la main, sinon pour le reste, suivez le guide:
# Compilation avec le Makfile > make tpsdl ocamlopt -I +sdl -I +site-lib/sdl bigarray.cmxa sdl.cmxa sdlloader.cmxa -o tpsdl tpsdl.ml > ls Makefile old_tags tpsdl.cmx _tags tpsdl tpsdl.ml foret_arbre_016.bmp tpsdl.cmi tpsdl.o # Pour faire le ménage > make clean # Avec ocamlbuild > ocamlbuild -use-ocamlfind tpsdl.native Finished, 4 targets (0 cached) in 00:00:00. > ls Makefile _tags old_tags tpsdl.native _build foret_arbre_016.bmp tpsdl.ml # Pour faire le ménage > ocamlbuild -clean
Il est possible que certaines méthodes ne marchent pas sur certaines installations: par exemple, la version avec ocamlbuild suppose que le binding SDL pour OCaml ai été installé avec le support pour ocamlfind (ce qui sera le cas pour les FreeBSD.)
Pour mes tests, j'ai utilisé le fichier d'exemple suivant: media:foret_arbre_016.bmp
Passage en niveau de gris
Le but est maintenant de passer une image en niveau de gris. Pour ça nous utiliseront la formule de luminosité moyenne suivante:
SDL nous permet de récupérer la couleur d'un pixel sous la forme d'un triplé d'entier, où chaque entier représente une composante (rouge, vert et bleu.) La valeur de chaque composante est comprise entre 0 et 255.
Écrire la fonction suivante:
val level : int*int*int -> float
level (r,g,b) renvoie un réel entre 0 et 1 représentant l'intensité lumineuse du pixel (en utilisant la formule précédante.)
Écrire la fonction suivante:
val color2grey : int * int * int -> int * int * int
color2grey (r,g,b) renvoie un triplé gris en utilisant la fonction level précédente (n'oubliez pas de ramener le niveau exprimé par un réel entre 0 et 1, à un tripler d'entier entre 0 et 255.)
Parcourir l'image
SDL fournit l'accès pixel par pixel à l'image à l'aide de la fonction (entre autre):
val Sdlvideo.get_pixel_color : Sdlvideo.surface -> int -> int -> int * int * int
Il nous faut donc parcourir l'image pixel par pixel pour faire le passage en niveau de gris. Pour ça, nous allons utiliser les boucles for d'OCaml. Voici un petit exemple:
let affiche n = for i = 0 to n do print_int i; print_newline (); done
Pour le passage en niveau de gris, nous allons écrire dans une nouvelle surface à l'aide de la fonction:
val Sdlvideo.put_pixel_color : Sdlvideo.surface -> int -> int -> int * int * int -> unit
Sdlvideo.put_pixel_color img x y (r,g,b) met le pixel au coordonnées (x,y) à la couleur (r,g,b).
Écrire la fonction suivante:
val image2grey : Sdlvideo.surface -> Sdlvideo.surface -> unit
image2grey src dst parcours tous les pixels de l'image src et met le pixel correspondant dans dst en gris (à l'aide de color2grey.)
Programme final
Nous allons regrouper l'ensemble de notre code pour afficher notre image en niveau de gris.
Il ne nous reste que peut de chose à faire:
- créer une nouvelle surface avec le même format que l'original
- appeler notre fonction
- afficher la nouvelle surface.
Pour créer la nouvelle surface nous utiliserons: Sdlvideo.create_RGB_surface_format img [] w h (img étant notre surface et w et h les dimensions de celle-ci.)
Intégrer au code fourni l'affichage de l'image en niveau de gris.
Au final, votre programme devra:
- Prendre un nom de fichier sur la ligne de commande
- Afficher l'image correspondante
- Attendre une touche
- Créer la nouvelle surface
- Utiliser la fonction de conversion en niveau de gris
- Afficher le résultat
- Attendre une touche.