20131007:TP:OCaml:SDL

De wiki-prog
Aller à : navigation, rechercher

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: 0.3 * red + 0.59 * green + 0.11 * blue

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:

  1. Prendre un nom de fichier sur la ligne de commande
  2. Afficher l'image correspondante
  3. Attendre une touche
  4. Créer la nouvelle surface
  5. Utiliser la fonction de conversion en niveau de gris
  6. Afficher le résultat
  7. Attendre une touche.