// Auteurs
// Matthieu Aubry
// Anthony Combe
// Licence
// Domaine publique
Unit image_unit;

INTERFACE

uses interface_unit;

const DEBUG=FALSE;

// CONSTANTES
      LMAX = 2400;
      HMAX = 2400;
      


// TYPES
type
    // pixel
    Tpixel = record
            red : byte;
            green : byte;
            blue : byte
            end;
    // image : largeur*hauteur et tableau de pixel
    Timage = record
            hauteur : integer;
            largeur : integer;
            pixels : array[0..LMAX,0..HMAX] of Tpixel
            end;
    // pointeur vers image
    pTimage = ^Timage;
    
    

// FONCTIONS
function new_image:pTimage;
function read_pixel(x,y:integer;img:pTimage):Tpixel;
function read_image(name:string):pTimage;
function somme_image(img1, img2 : pTimage) : pTimage;
function produit_image(img1, img2 : pTimage) : pTimage;
function difference_image(img1, img2 : pTimage) : pTimage;
function min(a,b : byte) : byte;
function max(a,b : byte) : byte;
function min_image(img1, img2 : pTimage) : pTimage;
function max_image(img1, img2 : pTimage) : pTimage;
function grey_level(img : pTimage) : pTimage;
function reverse_color(img : pTimage) : pTimage;

// PROCEDURES
procedure write_image(img:pTimage ; name:string; mat:pguimat; av1,av2:longint; etape:integer);
procedure delete_image(image:pTimage);
procedure write_pixel(x,y:integer;img:pTimage;red,green,blue:byte);


IMPLEMENTATION

//
// alloue l'espace m�oire d'une image
// @return poiteur vers l'image
function new_image:pTimage;
var image : pTimage;
begin
     new(image);
     new_image := image;
end;

//
// supprime une image en m�oire
// @param image : pointeur vers l'image �supprimer
procedure delete_image(image:pTimage);
begin
     dispose(image);
end;

//
// �rit un pixel dans l'image 
// @param x,y : coordonn�s d'�riture du pixel
// @param img : pointeur vers l'image
// @param red, green, blue : composantes RGB du pixel 
procedure write_pixel(x,y:integer;img:pTimage;red,green,blue:byte);
begin
    if (x < 0) or (y>img^.hauteur) or (y < 0) or (x>img^.largeur) then //writeln('Erreur position ecriture : ',x,'*',y)
    else
        begin
        img^.pixels[x, y].red := red;
        img^.pixels[x, y].green := green;
        img^.pixels[x, y].blue := blue;
        end;
end;

//
// lit un pixel dans l'image 
// @param x,y : coordonn�s de lecture du pixel
// @param img : pointeur vers l'image
// @return pixel lu
function read_pixel(x,y:integer;img:pTimage):Tpixel;
var pix:Tpixel;
begin
    if (x < 0) or (y>img^.hauteur) or (y < 0) or (x>img^.largeur) then
    begin
       //writeln('Erreur lecture pixel');
       pix.blue:=0;
       pix.red:=0;
       pix.green:=0;
    end
    else pix := img^.pixels[x, y];
    read_pixel:=pix;
end;

//
// lit une image au format PPM sur le disque dur et renvoie un pointeur vers l'image
// @param name : nom de l'image �lire sur le disque (ex. "./petanque.ppm")
// @return pointeur vers l'image lue
function read_image(name:string):pTimage;
var fichiert: text;
    fichierb: file of byte;
    i,j, height, width, offset : integer;
    line, dim:string[255];
    img:pTimage;

begin
     
     if(DEBUG) then writeln('Lecture image');
     img := new_image;
     line:='';
     offset:=0;

     // ouverture mode texte pour lire les headers
     assign(fichiert, name );
     reset(fichiert);
     if(IORESULT<>0) then writeln('ERREUR');

     // calcul taille offset
     // prob si une dim == 255 ?
     while (line<>'255') do
     begin
          dim := line;
          readln(fichiert, line);
          // on ajoute 1 car la lenght ne compte pas les retour �la ligne
          offset:=offset+length(line)+1;
     end;
     
     i:=1;
     height:=0;
     width:=0;
     
     // on trouve la largeur
     while (dim[i]<>' ') do
     begin
          width:=width*10+ord(dim[i])-48;
          inc(i);
     end;
     inc(i);
     
     // puis la hauteur
     while(i<=length(dim)) do  
     begin

          height:=height*10+ord(dim[i])-48;
          inc(i);
     end;
     
     // on ferme le fichier
     close(fichiert);

     // affichage console
     if(DEBUG) then writeln('Nom : ',name);
     if(DEBUG) then writeln('Dimensions : ',width,'x',height);

     // on assigne �l'image ses caract�istiques
     img^.hauteur := height;
     img^.largeur := width;

     // ouverture mode binaire pour lire les byte des pixels de l'image
     assign(fichierb,name);
     reset(fichierb);

     // on se place apr� le header
     seek(fichierb, offset);
     i:=0;
     j:=0;
     
     // pour chaque ligne
     while (j<img^.hauteur) do
     begin
        // on lit successivement R,G,B
        case i MOD 3 of
            0:read(fichierb,img^.pixels[i DIV 3,j].red);
            1:read(fichierb,img^.pixels[i DIV 3,j].green);
            2:read(fichierb,img^.pixels[i DIV 3,j].blue);
            end;
    
        inc(i);
        // saut de ligne
        if i= img^.largeur*3 then
        begin
            i := 0;
            inc(j);
        end;

     end;
                  
     if(DEBUG) then writeln('/Lecture image');
     if(DEBUG) then writeln;
     read_image:=img;
end;

//
// �rit une image au format PPM sur le disque dur 
// @param img : pointeur vers l'image ��rire
// @param name : nom de l'image ��rire sur le disque (ex. "./petanque.ppm")
procedure write_image(img:pTimage ; name:string; mat:pguimat; av1,av2:longint; etape:integer);
var fichiert:text;
    h,l:string;
    fichierb: file of byte;
    i,j,offset:integer;

begin

    // affichage console pour debug
    if(DEBUG) then writeln('Ecriture image');
    if(DEBUG) then writeln('Nom : ',name);      
    if(DEBUG) then writeln('Dimensions : ',img^.largeur,'x',img^.hauteur);
    
    // ouverture du fichier mode texte
    assign(fichiert, name);
    rewrite(fichiert);
    
    l:='';
    h:='';
    
    // on convertit les dimensions de l'image de type integer en chaine de caract�es
    str(img^.largeur,l);
    str(img^.hauteur,h);

    // cr�tion des headers
    write(fichiert,'P6');
    write(fichiert,chr(10));
    write(fichiert,'# created by XtreMProgrammers'); // XtremProgrammers : c'est nous
    write(fichiert,chr(10));
    write(fichiert,l); // largeur
    write(fichiert,' ');
    write(fichiert,h); // hauteur
    write(fichiert,chr(10));
    write(fichiert,'255'); // flag 255
    write(fichiert,chr(10));
                                    
    // calcul de la taille du header pour connaitre l'offset du d�ut des donn�s
    offset:=length('P6')+length('# created by XtreMProgrammers')+length(l)+length(' ')+length(h)+length('255')+4;

    // fermeture du fichier en mode texte
    close(fichiert);
    
    // ouverture mode binaire
    assign(fichierb, name);
    reset(fichierb);
    seek(fichierb,offset);

    i:=0;
    j:=0;
    
    // �riture des composantes RGB de chaque pixel
    while(j<img^.hauteur) do
    begin
        // actualisation de la barre de d�ilement
        geretempsfonction(mat, av1, av2, img^.hauteur * img^.largeur *3-1, etape);
    
        case i MOD 3 of
            0:write(fichierb,img^.pixels[i DIV 3,j].red);
            1:write(fichierb,img^.pixels[i DIV 3,j].green);
            2:write(fichierb,img^.pixels[i DIV 3,j].blue);
            end;

        inc(i);
        if i= img^.largeur*3 then
        begin
            i := 0;
            inc(j);
        end;

    end;

    // fermeture
    close(fichierb);       
    if(DEBUG) then writeln('/Ecriture image');
    if(DEBUG) then writeln;
end;

//
// effectue la somme de 2 images
// @param img1, img2 : les 2 images �sommer, de m�es dimensions
// @return poiteur vers l'image r�ultante
function somme_image(img1, img2 : pTimage) : pTimage;
var img : pTimage;
    i,j : integer;
begin
    // v�ifie dimensions
    if (img1^.hauteur=img2^.hauteur) AND (img1^.largeur=img2^.largeur) then
        begin 
            img := new_image;
            img^.hauteur := img1^.hauteur; 
            img^.largeur := img1^.largeur;
            // parcourt en x/y
            for i:=0 to img^.largeur Do
                for j:= 0 to img^.hauteur Do
                    begin
                    
                    // R
                    if img1^.pixels[i,j].red + img2^.pixels[i,j].red <= 255 then
                        img^.pixels[i,j].red := img1^.pixels[i,j].red + img2^.pixels[i,j].red
                    else img^.pixels[i,j].red := 255;   

                    // G
                    if img1^.pixels[i,j].green + img2^.pixels[i,j].green <= 255 then
                        img^.pixels[i,j].green := img1^.pixels[i,j].green + img2^.pixels[i,j].green
                    else img^.pixels[i,j].green := 255;

                    // B
                    if img1^.pixels[i,j].blue + img2^.pixels[i,j].blue <= 255 then
                        img^.pixels[i,j].blue := img1^.pixels[i,j].blue + img2^.pixels[i,j].blue
                    else img^.pixels[i,j].blue := 255;

                end;
        end
        else img := NIL;

        somme_image := img;
end;

//
// effectue le produit de 2 images
// @param img1, img2 : les 2 images �multiplier, de m�es dimensions
// @return poiteur vers l'image r�ultante
function produit_image(img1, img2 : pTimage) : pTimage;
var img : pTimage;
    i,j : integer;
begin
    // v�ifie dimensions
    if (img1^.hauteur=img2^.hauteur) AND (img1^.largeur=img2^.largeur) then
    begin 
        img := new_image;
        img^.hauteur := img1^.hauteur; 
        img^.largeur := img1^.largeur;
        // parcourt x/y
        for i:=0 to img^.largeur Do
            for j:= 0 to img^.hauteur Do
                begin
    
                // R
                if img1^.pixels[i,j].red * img2^.pixels[i,j].red <= 255 then
                img^.pixels[i,j].red := img1^.pixels[i,j].red * img2^.pixels[i,j].red
                else img^.pixels[i,j].red := 255;   
    
                // G
                if img1^.pixels[i,j].green * img2^.pixels[i,j].green <= 255 then
                img^.pixels[i,j].green := img1^.pixels[i,j].green * img2^.pixels[i,j].green
                else img^.pixels[i,j].green := 255;
    
                // B
                if img1^.pixels[i,j].blue * img2^.pixels[i,j].blue <= 255 then
                img^.pixels[i,j].blue := img1^.pixels[i,j].blue * img2^.pixels[i,j].blue
                else img^.pixels[i,j].blue := 255;
    
           end;    
    end
    else img := NIL;
    
    produit_image := img;
end;

//
// effectue la diff�ence de 2 images
// @param img1, img2 : les 2 images �soustraire, de m�es dimensions
// @return poiteur vers l'image r�ultante
function difference_image(img1, img2 : pTimage) : pTimage;
var img : pTimage;
    i,j : integer;
begin
    // v�ifie dimensions
    if (img1^.hauteur=img2^.hauteur) AND (img1^.largeur=img2^.largeur) then
        begin 
            img := new_image;
            img^.hauteur := img1^.hauteur; 
            img^.largeur := img1^.largeur;
            
            // parcourt x/y
            for i:=0 to img^.largeur Do
                for j:= 0 to img^.hauteur Do
                    begin

                    // R
                    if img1^.pixels[i,j].red - img2^.pixels[i,j].red >= 0 then
                        img^.pixels[i,j].red := img1^.pixels[i,j].red - img2^.pixels[i,j].red
                    else img^.pixels[i,j].red := 0;

                    // G
                    if img1^.pixels[i,j].green - img2^.pixels[i,j].green >= 0 then
                        img^.pixels[i,j].green := img1^.pixels[i,j].green - img2^.pixels[i,j].green
                    else img^.pixels[i,j].green := 0;

                    // B
                    if img1^.pixels[i,j].blue - img2^.pixels[i,j].blue >= 0 then
                        img^.pixels[i,j].blue := img1^.pixels[i,j].blue - img2^.pixels[i,j].blue
                    else img^.pixels[i,j].blue := 0;

                end;
        end
        else img := NIL;

        difference_image := img;
end;

//
// minimum de byte
// @param a,b : nombres 
// @return minimum entre a et b
function min(a,b : byte) : byte;
begin
     if a>b then min := b
     else min := a;
end;

//
// maximum de byte
// @param a,b : nombres 
// @return maximum entre a et b
function max(a,b : byte) : byte;
begin
     if a>b then max := a
     else max := b;
end;

//
// calcule l'image telle que chaque composante est le minimum entre les 2 images img1/img2
// @param img1, img2 : les 2 images utilis�s pour le minimum
// @return poiteur vers l'image r�ultante
function min_image(img1, img2 : pTimage) : pTimage;
var img : pTimage;
    i,j : integer;
    pixel1, pixel2 : Tpixel;
begin
    // v�ifie dimensions
    if (img1^.hauteur=img2^.hauteur) AND (img1^.largeur=img2^.largeur) then
    begin 
            img := new_image;
            img^.hauteur := img1^.hauteur; 
            img^.largeur := img1^.largeur;
            // parcourt x/y
            for i:=0 to img^.largeur Do
                for j:= 0 to img^.hauteur Do
                    begin
                    
                    // pixels images sources
                    pixel1 := read_pixel(i,j,img1);
                    pixel2 := read_pixel(i,j,img2);
                    
                    // on �rit le pixel minimum
                    write_pixel(i, j, img, min(pixel1.red , pixel2.red), min(pixel1.green , pixel2.green), min(pixel1.blue , pixel2.blue) );
                    end;
    end
    else img := NIL;
    min_image := img;
end;

//
// calcule l'image telle que chaque composante est le maximum entre les 2 images img1/img2
// @param img1, img2 : les 2 images utilis�s pour le maximum
// @return poiteur vers l'image r�ultante
function max_image(img1, img2 : pTimage) : pTimage;
var img : pTimage;
    i,j : integer;
    pixel1, pixel2 : Tpixel;
begin
    // v�ifie dimensions
    if (img1^.hauteur=img2^.hauteur) AND (img1^.largeur=img2^.largeur) then
    begin 
            img := new_image;
            img^.hauteur := img1^.hauteur; 
            img^.largeur := img1^.largeur;
            // parcourt x/y
            for i:=0 to img^.largeur Do
                for j:= 0 to img^.hauteur Do
                    begin
                    
                    // pixels images sources
                    pixel1 := read_pixel(i,j,img1);
                    pixel2 := read_pixel(i,j,img2);
                    
                    // on �rit le pixel minimum
                    write_pixel(i, j, img, max(pixel1.red , pixel2.red), max(pixel1.green , pixel2.green), max(pixel1.blue , pixel2.blue) );
                    end;
    end
    else img := NIL;
    max_image := img;
end;

//
// convertit une image en niveaux de gris
// @param img : pointeur vers l'image �convertir en niveaux de gris
// @return pointeur vers l'image convertie
function grey_level(img : pTimage) : pTimage;
Var i,j : integer;
    comp : byte;
    img2 : pTimage;
begin
    // image cible
    img2 := new_image;
    img2^.hauteur := img^.hauteur;
    img2^.largeur := img^.largeur;
    
    // parcourt x/y
    for i:=0 to img^.largeur Do
        for j:= 0 to img^.hauteur Do
            begin
            
            // calcul du pixel en gris : moyenne des 3 composantes RGB
            comp := (read_pixel(i,j,img).red+read_pixel(i,j,img).green+read_pixel(i,j,img).blue) DIV 3;
            write_pixel(i,j,img2, comp, comp, comp);
            end;
    grey_level := img2;
end;

//
// convertit les couleurs de l'image en inverse couleurs
// @param img : poiteur vers l'image �convertir
// @return pointeur vers l'image dont les couleurs sont invers�s
function reverse_color(img : pTimage) : pTimage;
Var i,j : integer;
    img2 : pTimage;
begin
     img2 := new_image;
     img2^.hauteur := img^.hauteur;
     img2^.largeur := img^.largeur;
     for i:=0 to img^.largeur Do
         for j:= 0 to img^.hauteur Do
             begin
             write_pixel(i,j,img2, 255-read_pixel(i,j,img).red, 255-read_pixel(i,j,img).green, 255-read_pixel(i,j,img).blue);
             end;
     reverse_color := img2;
end;

//
// main
Begin
End.
