diff --git a/graph.c b/graph.c index 2383cdba..b4d82458 100644 --- a/graph.c +++ b/graph.c @@ -1,2431 +1,4864 @@ -/* Grafx2 - The Ultimate 256-color bitmap paint program - - Copyright 2008 Franck Charlet - Copyright 2007 Adrien Destugues - Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) - - Grafx2 is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; version 2 - of the License. - - Grafx2 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Grafx2; if not, see or - write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -******************************************************************************** - - Drawing functions and effects. - -*/ - -#include -#include -#include - -#include "global.h" -#include "struct.h" -#include "moteur.h" -#include "boutons.h" -#include "pages.h" -#include "erreurs.h" -#include "sdlscreen.h" -#include "graph.h" -#include "divers.h" -#include "pxsimple.h" -#include "pxtall.h" -#include "pxwide.h" -#include "windows.h" - -// Fonction qui met à jour la zone de l'image donnée en paramètre sur l'écran. -// Tient compte du décalage X et Y et du zoom, et fait tous les controles nécessaires -void Mettre_Ecran_A_Jour(short X, short Y, short Largeur, short Hauteur) -{ - short L_effectif, H_effectif; - short X_effectif; - short Y_effectif; - short Diff; - - // Première étape, si L ou H est négatif, on doit remettre la zone à l'endroit - if (Largeur < 0) - { - X += Largeur; - Largeur = - Largeur; - } - - if (Hauteur < 0) - { - Y += Hauteur; - Hauteur = - Hauteur; - } - - // D'abord on met à jour dans la zone écran normale - Diff = X-Principal_Decalage_X; - if (Diff<0) - { - L_effectif = Largeur + Diff; - X_effectif = 0; - } - else - { - L_effectif = Largeur; - X_effectif = Diff; - } - Diff = Y-Principal_Decalage_Y; - if (Diff<0) - { - H_effectif = Hauteur + Diff; - Y_effectif = 0; - } - else - { - H_effectif = Hauteur; - Y_effectif = Diff; - } - - // Normalement il ne faudrait pas updater au delà du split quand on est en mode loupe, - // mais personne ne devrait demander d'update en dehors de cette limite, même le fill est contraint - // a rester dans la zone visible de l'image - // ...Sauf l'affichage de brosse en preview - yr - if(Loupe_Mode && X_effectif + L_effectif > Principal_Split) - L_effectif = Principal_Split - X_effectif; - else if(X_effectif + L_effectif > Largeur_ecran) - L_effectif = Largeur_ecran - X_effectif; - - if(Y_effectif + H_effectif > Menu_Ordonnee) - H_effectif = Menu_Ordonnee - Y_effectif; - /* - SDL_Rect r; - r.x=X_effectif; - r.y=Y_effectif; - r.h=H_effectif; - r.w=L_effectif; - SDL_FillRect(Ecran_SDL,&r,3); - */ - UpdateRect(X_effectif,Y_effectif,L_effectif,H_effectif); - - // Et ensuite dans la partie zoomée - if(Loupe_Mode) - { - // Clipping en X - X_effectif = (X-Loupe_Decalage_X)*Loupe_Facteur; - Y_effectif = (Y-Loupe_Decalage_Y)*Loupe_Facteur; - L_effectif = Largeur * Loupe_Facteur; - H_effectif = Hauteur * Loupe_Facteur; - - if (X_effectif < 0) - { - L_effectif+=X_effectif; - if (L_effectif<0) - return; - - X_effectif = Principal_Split + LARGEUR_BARRE_SPLIT*Menu_Facteur_X; - } - else - X_effectif += Principal_Split + LARGEUR_BARRE_SPLIT*Menu_Facteur_X; - Diff = X_effectif+L_effectif-Largeur_ecran; - if (Diff>0) - { - L_effectif -=Diff; - if (L_effectif<0) - return; - } - - - // Clipping en Y - if (Y_effectif < 0) - { - H_effectif+=Y_effectif; - if (H_effectif<0) - return; - Y_effectif = 0; - } - Diff = Y_effectif+H_effectif-Menu_Ordonnee; - if (Diff>0) - { - H_effectif -=Diff; - if (H_effectif<0) - return; - } - - // Très utile pour le debug :) - /*SDL_Rect r; - r.x=X_effectif; - r.y=Y_effectif; - r.h=H_effectif; - r.w=L_effectif; - SDL_FillRect(Ecran_SDL,&r,3);*/ - - UpdateRect(X_effectif,Y_effectif,L_effectif,H_effectif); - } -} - - - -void Transformer_point(short X, short Y, float cosA, float sinA, - short * Xr, short * Yr) -{ - *Xr=Round(((float)X*cosA)+((float)Y*sinA)); - *Yr=Round(((float)Y*cosA)-((float)X*sinA)); -} - - - -//--------------------- Initialisation d'un mode vidéo ----------------------- - -void Initialiser_mode_video(int Largeur, int Hauteur, int Fullscreen) -{ - int Sensibilite_X; - int Sensibilite_Y; - int Indice; - int Facteur; - - if (Largeur_ecran!=Largeur || - Hauteur_ecran!=Hauteur || - Mode_video[Resolution_actuelle].Fullscreen != Fullscreen) - { - switch (Pixel_ratio) - { - case PIXEL_SIMPLE: - Pixel_width=1; - Pixel_height=1; - Pixel = Pixel_Simple ; - Lit_pixel= Lit_Pixel_Simple ; - Display_screen = Afficher_partie_de_l_ecran_Simple ; - Block = Block_Simple ; - Pixel_Preview_Normal = Pixel_Preview_Normal_Simple ; - Pixel_Preview_Loupe = Pixel_Preview_Loupe_Simple ; - Ligne_horizontale_XOR = Ligne_horizontale_XOR_Simple ; - Ligne_verticale_XOR = Ligne_verticale_XOR_Simple ; - Display_brush_Color = Display_brush_Color_Simple ; - Display_brush_Mono = Display_brush_Mono_Simple ; - Clear_brush = Clear_brush_Simple ; - Remap_screen = Remap_screen_Simple ; - Afficher_ligne = Afficher_une_ligne_ecran_Simple ; - Afficher_ligne_fast = Afficher_une_ligne_ecran_Simple ; - Lire_ligne = Lire_une_ligne_ecran_Simple ; - Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Simple ; - Display_brush_Color_zoom = Display_brush_Color_zoom_Simple ; - Display_brush_Mono_zoom = Display_brush_Mono_zoom_Simple ; - Clear_brush_zoom = Clear_brush_zoom_Simple ; - Affiche_brosse = Affiche_brosse_Simple ; - break; - case PIXEL_TALL: - Pixel_width=1; - Pixel_height=2; - Pixel = Pixel_Tall; - Lit_pixel= Lit_Pixel_Tall; - Display_screen = Afficher_partie_de_l_ecran_Tall; - Block = Block_Tall; - Pixel_Preview_Normal = Pixel_Preview_Normal_Tall; - Pixel_Preview_Loupe = Pixel_Preview_Loupe_Tall; - Ligne_horizontale_XOR = Ligne_horizontale_XOR_Tall; - Ligne_verticale_XOR = Ligne_verticale_XOR_Tall; - Display_brush_Color = Display_brush_Color_Tall; - Display_brush_Mono = Display_brush_Mono_Tall; - Clear_brush = Clear_brush_Tall; - Remap_screen = Remap_screen_Tall; - Afficher_ligne = Afficher_une_ligne_ecran_Tall; - Afficher_ligne_fast = Afficher_une_ligne_ecran_Tall; - Lire_ligne = Lire_une_ligne_ecran_Tall; - Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Tall; - Display_brush_Color_zoom = Display_brush_Color_zoom_Tall; - Display_brush_Mono_zoom = Display_brush_Mono_zoom_Tall; - Clear_brush_zoom = Clear_brush_zoom_Tall; - Affiche_brosse = Affiche_brosse_Tall; - break; - case PIXEL_WIDE: - Pixel_width=2; - Pixel_height=1; - Pixel = Pixel_Wide ; - Lit_pixel= Lit_Pixel_Wide ; - Display_screen = Afficher_partie_de_l_ecran_Wide ; - Block = Block_Wide ; - Pixel_Preview_Normal = Pixel_Preview_Normal_Wide ; - Pixel_Preview_Loupe = Pixel_Preview_Loupe_Wide ; - Ligne_horizontale_XOR = Ligne_horizontale_XOR_Wide ; - Ligne_verticale_XOR = Ligne_verticale_XOR_Wide ; - Display_brush_Color = Display_brush_Color_Wide ; - Display_brush_Mono = Display_brush_Mono_Wide ; - Clear_brush = Clear_brush_Wide ; - Remap_screen = Remap_screen_Wide ; - Afficher_ligne = Afficher_une_ligne_ecran_Wide ; - Afficher_ligne_fast = Afficher_une_ligne_ecran_fast_Wide ; - Lire_ligne = Lire_une_ligne_ecran_Wide ; - Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Wide ; - Display_brush_Color_zoom = Display_brush_Color_zoom_Wide ; - Display_brush_Mono_zoom = Display_brush_Mono_zoom_Wide ; - Clear_brush_zoom = Clear_brush_zoom_Wide ; - Affiche_brosse = Affiche_brosse_Wide ; - break; - } - // Valeurs raisonnables: minimum 320x200 - if (Pixel_width==1 && Pixel_height==1) - { - if (Largeur < 320) - Largeur = 320; - if (Hauteur < 200) - Hauteur = 200; - } - else - { - if (Largeur < 640) - Largeur = 640; - if (Hauteur < 400) - Hauteur = 400; - } +/* Grafx2 - The Ultimate 256-color bitmap paint program + + + + Copyright 2008 Franck Charlet + + Copyright 2007 Adrien Destugues + + Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) + + + + Grafx2 is free software; you can redistribute it and/or + + modify it under the terms of the GNU General Public License + + as published by the Free Software Foundation; version 2 + + of the License. + + + + Grafx2 is distributed in the hope that it will be useful, + + but WITHOUT ANY WARRANTY; without even the implied warranty of + + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public License + + along with Grafx2; if not, see or + + write to the Free Software Foundation, Inc., + + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + + +******************************************************************************** + + + + Drawing functions and effects. + + + +*/ + + + +#include + +#include + +#include + + + +#include "global.h" + +#include "struct.h" + +#include "moteur.h" + +#include "boutons.h" + +#include "pages.h" + +#include "erreurs.h" + +#include "sdlscreen.h" + +#include "graph.h" + +#include "divers.h" + +#include "pxsimple.h" + +#include "pxtall.h" + +#include "pxwide.h" + +#include "windows.h" + + + +// Fonction qui met à jour la zone de l'image donnée en paramètre sur l'écran. + +// Tient compte du décalage X et Y et du zoom, et fait tous les controles nécessaires + +void Mettre_Ecran_A_Jour(short X, short Y, short Largeur, short Hauteur) + +{ + + short L_effectif, H_effectif; + + short X_effectif; + + short Y_effectif; + + short Diff; + + + + // Première étape, si L ou H est négatif, on doit remettre la zone à l'endroit + + if (Largeur < 0) + + { + + X += Largeur; + + Largeur = - Largeur; + + } + + + + if (Hauteur < 0) + + { + + Y += Hauteur; + + Hauteur = - Hauteur; + + } + + + + // D'abord on met à jour dans la zone écran normale + + Diff = X-Principal_Decalage_X; + + if (Diff<0) + + { + + L_effectif = Largeur + Diff; + + X_effectif = 0; + + } + + else + + { + + L_effectif = Largeur; + + X_effectif = Diff; + + } + + Diff = Y-Principal_Decalage_Y; + + if (Diff<0) + + { + + H_effectif = Hauteur + Diff; + + Y_effectif = 0; + + } + + else + + { + + H_effectif = Hauteur; + + Y_effectif = Diff; + + } + + + + // Normalement il ne faudrait pas updater au delà du split quand on est en mode loupe, + + // mais personne ne devrait demander d'update en dehors de cette limite, même le fill est contraint + + // a rester dans la zone visible de l'image + + // ...Sauf l'affichage de brosse en preview - yr + + if(Loupe_Mode && X_effectif + L_effectif > Principal_Split) + + L_effectif = Principal_Split - X_effectif; + + else if(X_effectif + L_effectif > Largeur_ecran) + + L_effectif = Largeur_ecran - X_effectif; + + + + if(Y_effectif + H_effectif > Menu_Ordonnee) + + H_effectif = Menu_Ordonnee - Y_effectif; + + /* + + SDL_Rect r; + + r.x=X_effectif; + + r.y=Y_effectif; + + r.h=H_effectif; + + r.w=L_effectif; + + SDL_FillRect(Ecran_SDL,&r,3); + + */ + + UpdateRect(X_effectif,Y_effectif,L_effectif,H_effectif); + + + + // Et ensuite dans la partie zoomée + + if(Loupe_Mode) + + { + + // Clipping en X + + X_effectif = (X-Loupe_Decalage_X)*Loupe_Facteur; + + Y_effectif = (Y-Loupe_Decalage_Y)*Loupe_Facteur; + + L_effectif = Largeur * Loupe_Facteur; + + H_effectif = Hauteur * Loupe_Facteur; + + + + if (X_effectif < 0) + + { + + L_effectif+=X_effectif; + + if (L_effectif<0) + + return; + + + + X_effectif = Principal_Split + LARGEUR_BARRE_SPLIT*Menu_Facteur_X; + + } + + else + + X_effectif += Principal_Split + LARGEUR_BARRE_SPLIT*Menu_Facteur_X; + + Diff = X_effectif+L_effectif-Largeur_ecran; + + if (Diff>0) + + { + + L_effectif -=Diff; + + if (L_effectif<0) + + return; + + } + + + + + + // Clipping en Y + + if (Y_effectif < 0) + + { + + H_effectif+=Y_effectif; + + if (H_effectif<0) + + return; + + Y_effectif = 0; + + } + + Diff = Y_effectif+H_effectif-Menu_Ordonnee; + + if (Diff>0) + + { + + H_effectif -=Diff; + + if (H_effectif<0) + + return; + + } + + + + // Très utile pour le debug :) + + /*SDL_Rect r; + + r.x=X_effectif; + + r.y=Y_effectif; + + r.h=H_effectif; + + r.w=L_effectif; + + SDL_FillRect(Ecran_SDL,&r,3);*/ + + + + UpdateRect(X_effectif,Y_effectif,L_effectif,H_effectif); + + } + +} + + + + + + + +void Transformer_point(short X, short Y, float cosA, float sinA, + + short * Xr, short * Yr) + +{ + + *Xr=Round(((float)X*cosA)+((float)Y*sinA)); + + *Yr=Round(((float)Y*cosA)-((float)X*sinA)); + +} + + + + + + + +//--------------------- Initialisation d'un mode vidéo ----------------------- + + + +void Initialiser_mode_video(int Largeur, int Hauteur, int Fullscreen) + +{ + + int Sensibilite_X; + + int Sensibilite_Y; + + int Indice; + + int Facteur; + + + + if (Largeur_ecran!=Largeur || + + Hauteur_ecran!=Hauteur || + + Mode_video[Resolution_actuelle].Fullscreen != Fullscreen) + + { + + switch (Pixel_ratio) + + { + + case PIXEL_SIMPLE: + + Pixel_width=1; + + Pixel_height=1; + + Pixel = Pixel_Simple ; + + Lit_pixel= Lit_Pixel_Simple ; + + Display_screen = Afficher_partie_de_l_ecran_Simple ; + + Block = Block_Simple ; + + Pixel_Preview_Normal = Pixel_Preview_Normal_Simple ; + + Pixel_Preview_Loupe = Pixel_Preview_Loupe_Simple ; + + Ligne_horizontale_XOR = Ligne_horizontale_XOR_Simple ; + + Ligne_verticale_XOR = Ligne_verticale_XOR_Simple ; + + Display_brush_Color = Display_brush_Color_Simple ; + + Display_brush_Mono = Display_brush_Mono_Simple ; + + Clear_brush = Clear_brush_Simple ; + + Remap_screen = Remap_screen_Simple ; + + Afficher_ligne = Afficher_une_ligne_ecran_Simple ; + + Afficher_ligne_fast = Afficher_une_ligne_ecran_Simple ; + + Lire_ligne = Lire_une_ligne_ecran_Simple ; + + Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Simple ; + + Display_brush_Color_zoom = Display_brush_Color_zoom_Simple ; + + Display_brush_Mono_zoom = Display_brush_Mono_zoom_Simple ; + + Clear_brush_zoom = Clear_brush_zoom_Simple ; + + Affiche_brosse = Affiche_brosse_Simple ; + + break; + + case PIXEL_TALL: + + Pixel_width=1; + + Pixel_height=2; + + Pixel = Pixel_Tall; + + Lit_pixel= Lit_Pixel_Tall; + + Display_screen = Afficher_partie_de_l_ecran_Tall; + + Block = Block_Tall; + + Pixel_Preview_Normal = Pixel_Preview_Normal_Tall; + + Pixel_Preview_Loupe = Pixel_Preview_Loupe_Tall; + + Ligne_horizontale_XOR = Ligne_horizontale_XOR_Tall; + + Ligne_verticale_XOR = Ligne_verticale_XOR_Tall; + + Display_brush_Color = Display_brush_Color_Tall; + + Display_brush_Mono = Display_brush_Mono_Tall; + + Clear_brush = Clear_brush_Tall; + + Remap_screen = Remap_screen_Tall; + + Afficher_ligne = Afficher_une_ligne_ecran_Tall; + + Afficher_ligne_fast = Afficher_une_ligne_ecran_Tall; + + Lire_ligne = Lire_une_ligne_ecran_Tall; + + Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Tall; + + Display_brush_Color_zoom = Display_brush_Color_zoom_Tall; + + Display_brush_Mono_zoom = Display_brush_Mono_zoom_Tall; + + Clear_brush_zoom = Clear_brush_zoom_Tall; + + Affiche_brosse = Affiche_brosse_Tall; + + break; + + case PIXEL_WIDE: + + Pixel_width=2; + + Pixel_height=1; + + Pixel = Pixel_Wide ; + + Lit_pixel= Lit_Pixel_Wide ; + + Display_screen = Afficher_partie_de_l_ecran_Wide ; + + Block = Block_Wide ; + + Pixel_Preview_Normal = Pixel_Preview_Normal_Wide ; + + Pixel_Preview_Loupe = Pixel_Preview_Loupe_Wide ; + + Ligne_horizontale_XOR = Ligne_horizontale_XOR_Wide ; + + Ligne_verticale_XOR = Ligne_verticale_XOR_Wide ; + + Display_brush_Color = Display_brush_Color_Wide ; + + Display_brush_Mono = Display_brush_Mono_Wide ; + + Clear_brush = Clear_brush_Wide ; + + Remap_screen = Remap_screen_Wide ; + + Afficher_ligne = Afficher_une_ligne_ecran_Wide ; + + Afficher_ligne_fast = Afficher_une_ligne_ecran_fast_Wide ; + + Lire_ligne = Lire_une_ligne_ecran_Wide ; + + Display_zoomed_screen = Afficher_partie_de_l_ecran_zoomee_Wide ; + + Display_brush_Color_zoom = Display_brush_Color_zoom_Wide ; + + Display_brush_Mono_zoom = Display_brush_Mono_zoom_Wide ; + + Clear_brush_zoom = Clear_brush_zoom_Wide ; + + Affiche_brosse = Affiche_brosse_Wide ; + + break; + + } + + // Valeurs raisonnables: minimum 320x200 + + if (Pixel_width==1 && Pixel_height==1) + + { + + if (Largeur < 320) + + Largeur = 320; + + if (Hauteur < 200) + + Hauteur = 200; + + } + + else + + { + + if (Largeur < 640) + + Largeur = 640; + + if (Hauteur < 400) + + Hauteur = 400; + + } + // La largeur doit être un multiple de 4 +#ifdef __amigaos4__ + Largeur = (Largeur + 15) & 0xFFFFFFF0; +#else Largeur = (Largeur + 3 ) & 0xFFFFFFFC; - Set_Mode_SDL(&Largeur, &Hauteur,Fullscreen); - Largeur_ecran = Largeur/Pixel_width; - Hauteur_ecran = Hauteur/Pixel_height; - - // Taille des menus - if (Largeur_ecran/320 > Hauteur_ecran/200) - Facteur=Hauteur_ecran/200; - else - Facteur=Largeur_ecran/320; - - switch (Config.Ratio) - { - case 1: // adapter tout - Menu_Facteur_X=Facteur; - Menu_Facteur_Y=Facteur; - break; - case 2: // adapter légèrement - Menu_Facteur_X=Facteur-1; - if (Menu_Facteur_X<1) Menu_Facteur_X=1; - Menu_Facteur_Y=Facteur-1; - if (Menu_Facteur_Y<1) Menu_Facteur_Y=1; - break; - default: // ne pas adapter - Menu_Facteur_X=1; - Menu_Facteur_Y=1; - } - if (Pixel_height>Pixel_width) - Menu_Facteur_X*=2; - else if (Pixel_width>Pixel_height) - Menu_Facteur_Y*=2; - if (Buffer_de_ligne_horizontale) - free(Buffer_de_ligne_horizontale); - - Buffer_de_ligne_horizontale=(byte *)malloc(Pixel_width*((Largeur_ecran>Principal_Largeur_image)?Largeur_ecran:Principal_Largeur_image)); - - Set_palette(Principal_Palette); - - if (!Fullscreen) - Resolution_actuelle=0; - else - for (Indice=1; Indice> 3; - Menu_Ordonnee = Hauteur_ecran; - if (Menu_visible) - Menu_Ordonnee -= HAUTEUR_MENU * Menu_Facteur_Y; - Menu_Ordonnee_Texte = Hauteur_ecran-(Menu_Facteur_Y<<3); - Bouton[BOUTON_CHOIX_COL].Largeur=(Menu_Taille_couleur<<3)-1; - - Sensibilite_X = Config.Indice_Sensibilite_souris_X; - Sensibilite_Y = Config.Indice_Sensibilite_souris_Y; - Sensibilite_X>>=Mouse_Facteur_de_correction_X; - Sensibilite_Y>>=Mouse_Facteur_de_correction_Y; - Sensibilite_souris(Sensibilite_X?Sensibilite_X:1,Sensibilite_Y?Sensibilite_Y:1); - - Brouillon_Decalage_X=0; // | Il faut penser à éviter les incohérences - Brouillon_Decalage_Y=0; // |- de décalage du brouillon par rapport à - Brouillon_Loupe_Mode=0; // | la résolution. - } - if (Loupe_Mode) - { - Pixel_Preview=Pixel_Preview_Loupe; - } - else - { - Pixel_Preview=Pixel_Preview_Normal; - // Recaler la vue (meme clipping que dans Scroller_ecran()) - if (Principal_Decalage_X+Largeur_ecran>Principal_Largeur_image) - Principal_Decalage_X=Principal_Largeur_image-Largeur_ecran; - if (Principal_Decalage_X<0) - Principal_Decalage_X=0; - if (Principal_Decalage_Y+Menu_Ordonnee>Principal_Hauteur_image) - Principal_Decalage_Y=Principal_Hauteur_image-Menu_Ordonnee; - if (Principal_Decalage_Y<0) - Principal_Decalage_Y=0; - } - - Calculer_donnees_loupe(); - if (Loupe_Mode) - Recadrer_ecran_par_rapport_au_zoom(); - Calculer_limites(); - Calculer_coordonnees_pinceau(); - Afficher_ecran(); - - Resize_Largeur=0; - Resize_Hauteur=0; -} - - - - // -- Redimentionner l'image (nettoie l'écran virtuel) -- - -void Redimentionner_image(word Largeur_choisie,word Hauteur_choisie) -{ - word Ancienne_largeur=Principal_Largeur_image; - word Ancienne_hauteur=Principal_Hauteur_image; - - // +-+-+ - // |C| | A+B+C = Ancienne image - // +-+A| - // |B| | C = Nouvelle image - // +-+-+ - - if (Backup_avec_nouvelles_dimensions(1,Largeur_choisie,Hauteur_choisie)) - { - // La nouvelle page a pu être allouée, elle est pour l'instant pleine de - // 0s. Elle fait Principal_Largeur_image de large. - - // On copie donc maintenant la partie C dans la nouvelle image. - Copier_une_partie_d_image_dans_une_autre( - Ecran_backup,0,0,Min(Ancienne_largeur,Principal_Largeur_image), - Min(Ancienne_hauteur,Principal_Hauteur_image),Ancienne_largeur, - Principal_Ecran,0,0,Principal_Largeur_image); - } - else - { - // Afficher un message d'erreur - Afficher_curseur(); - Message_Memoire_insuffisante(); - Effacer_curseur(); - } -} - - - -void Remap_picture(void) -{ - short Pos_X; // Variable de balayage de la brosse - short Pos_Y; // Variable de balayage de la brosse - byte Utilisee[256]; // Tableau de booléens "La couleur est utilisée" - int Couleur; - - // On commence par initialiser le tableau de booléens à faux - for (Couleur=0;Couleur<=255;Couleur++) - Utilisee[Couleur]=0; - - // On calcule la table d'utilisation des couleurs - for (Pos_Y=0;Pos_YLimite_Gauche) && - (Lit_pixel_dans_ecran_courant(Debut_X-1,Ligne)==2)) || - // Test de la présence d'un point à droite du segment - ((Fin_X-1Limite_Haut)) - for (Pos_X=Debut_X;Pos_X*Limite_atteinte_Droite) - *Limite_atteinte_Droite=Fin_X; - // On remplit le segment de Debut_X à Fin_X-1. - for (Pos_X=Debut_X;Pos_XLimite_Haut) - Limite_courante_Haut--; - - for (Ligne=Limite_courante_Bas;Ligne>=Limite_courante_Haut;Ligne--) - { - Ligne_modifiee=0; - // On va traiter le cas de la ligne n° Ligne. - - // On commence le traitement à la gauche de l'écran - Debut_X=Limite_Gauche; - - // Pour chaque segment de couleur 1 que peut contenir la ligne - while (Debut_X<=Limite_Droite) - { - // On cherche son début - for (;(Debut_X<=Limite_Droite) && - (Lit_pixel_dans_ecran_courant(Debut_X,Ligne)!=1);Debut_X++); - - if (Debut_X<=Limite_Droite) - { - // Un segment de couleur 1 existe et commence à la position Debut_X. - // On va donc en chercher la fin. - for (Fin_X=Debut_X+1;(Fin_X<=Limite_Droite) && - (Lit_pixel_dans_ecran_courant(Fin_X,Ligne)==1);Fin_X++); - - // On sait qu'il existe un segment de couleur 1 qui commence en - // Debut_X et qui se termine en Fin_X-1. - - // On va maintenant regarder si une couleur sur la périphérie - // permet de colorier ce segment avec la couleur 2. - - Propagation_possible=( - // Test de la présence d'un point à gauche du segment - ((Debut_X>Limite_Gauche) && - (Lit_pixel_dans_ecran_courant(Debut_X-1,Ligne)==2)) || - // Test de la présence d'un point à droite du segment - ((Fin_X-1*Limite_atteinte_Droite) - *Limite_atteinte_Droite=Fin_X; - // On remplit le segment de Debut_X à Fin_X-1. - for (Pos_X=Debut_X;Pos_XLimite_Haut) ) - Limite_courante_Haut--; // On monte cette limite vers le haut - } - } - - *Limite_atteinte_Haut=Limite_courante_Haut; - *Limite_atteinte_Bas =Limite_courante_Bas; - (*Limite_atteinte_Droite)--; -} // Fin de la routine de remplissage "Fill" - - -void Remplir(byte Couleur_de_remplissage) -// -// Cette fonction fait un remplissage qui gère tous les effets. Elle fait -// appel à "Fill()". -// -{ - byte Forme_curseur_avant_remplissage; - byte * FX_Feedback_Ecran_avant_remplissage; - short Pos_X,Pos_Y; - short Limite_atteinte_Haut ,Limite_atteinte_Bas; - short Limite_atteinte_Gauche,Limite_atteinte_Droite; - byte Table_de_remplacement[256]; - - - // Avant toute chose, on vérifie que l'on n'est pas en train de remplir - // en dehors de l'image: - - if ( (Pinceau_X>=Limite_Gauche) && - (Pinceau_X<=Limite_Droite) && - (Pinceau_Y>=Limite_Haut) && - (Pinceau_Y<=Limite_Bas) ) - { - // On suppose que le curseur est déjà caché. - // Effacer_curseur(); - - // On va faire patienter l'utilisateur en lui affichant un joli petit - // sablier: - Forme_curseur_avant_remplissage=Forme_curseur; - Forme_curseur=FORME_CURSEUR_SABLIER; - Afficher_curseur(); - - // On commence par effectuer un backup de l'image. - Backup(); - - // On fait attention au Feedback qui DOIT se faire avec le backup. - FX_Feedback_Ecran_avant_remplissage=FX_Feedback_Ecran; - FX_Feedback_Ecran=Ecran_backup; - - // On va maintenant "épurer" la zone visible de l'image: - memset(Table_de_remplacement,0,256); - Table_de_remplacement[Lit_pixel_dans_ecran_courant(Pinceau_X,Pinceau_Y)]=1; - Remplacer_toutes_les_couleurs_dans_limites(Table_de_remplacement); - - // On fait maintenant un remplissage classique de la couleur 1 avec la 2 - Fill(&Limite_atteinte_Haut ,&Limite_atteinte_Bas, - &Limite_atteinte_Gauche,&Limite_atteinte_Droite); - - // On s'apprête à faire des opérations qui nécessitent un affichage. Il - // faut donc retirer de l'écran le curseur: - Effacer_curseur(); - Forme_curseur=Forme_curseur_avant_remplissage; - - // Il va maintenant falloir qu'on "turn" ce gros caca "into" un truc qui - // ressemble un peu plus à ce à quoi l'utilisateur peut s'attendre. - if (Limite_atteinte_Haut>Limite_Haut) - Copier_une_partie_d_image_dans_une_autre(Ecran_backup, // Source - Limite_Gauche,Limite_Haut, // Pos X et Y dans source - (Limite_Droite-Limite_Gauche)+1, // Largeur copie - Limite_atteinte_Haut-Limite_Haut,// Hauteur copie - Principal_Largeur_image, // Largeur de la source - Principal_Ecran, // Destination - Limite_Gauche,Limite_Haut, // Pos X et Y destination - Principal_Largeur_image); // Largeur destination - if (Limite_atteinte_BasLimite_Gauche) - Copier_une_partie_d_image_dans_une_autre(Ecran_backup, - Limite_Gauche,Limite_atteinte_Haut, - Limite_atteinte_Gauche-Limite_Gauche, - (Limite_atteinte_Bas-Limite_atteinte_Haut)+1, - Principal_Largeur_image,Principal_Ecran, - Limite_Gauche,Limite_atteinte_Haut,Principal_Largeur_image); - if (Limite_atteinte_Droite=Limite_Gauche) && - (Pos_X<=Limite_Droite) && - (Pos_Y>=Limite_Haut) && - (Pos_Y<=Limite_Bas) ) - Pixel_Preview(Pos_X,Pos_Y,Couleur); - } - // Affichage d'un point pour une preview, avec sa propre couleur - void Pixel_figure_Preview_auto(word Pos_X,word Pos_Y) - { - if ( (Pos_X>=Limite_Gauche) && - (Pos_X<=Limite_Droite) && - (Pos_Y>=Limite_Haut) && - (Pos_Y<=Limite_Bas) ) - Pixel_Preview(Pos_X,Pos_Y,Lit_pixel_dans_ecran_courant(Pos_X,Pos_Y)); - } - - // Affichage d'un point pour une preview en xor - void Pixel_figure_Preview_xor(word Pos_X,word Pos_Y,__attribute__((unused)) byte Couleur) - { - if ( (Pos_X>=Limite_Gauche) && - (Pos_X<=Limite_Droite) && - (Pos_Y>=Limite_Haut) && - (Pos_Y<=Limite_Bas) ) - Pixel_Preview(Pos_X,Pos_Y,~Lit_pixel(Pos_X-Principal_Decalage_X, - Pos_Y-Principal_Decalage_Y)); - } - - // Effacement d'un point de preview - void Pixel_figure_Effacer_preview(word Pos_X,word Pos_Y,__attribute__((unused)) byte Couleur) - { - if ( (Pos_X>=Limite_Gauche) && - (Pos_X<=Limite_Droite) && - (Pos_Y>=Limite_Haut) && - (Pos_Y<=Limite_Bas) ) - Pixel_Preview(Pos_X,Pos_Y,Lit_pixel_dans_ecran_courant(Pos_X,Pos_Y)); - } - - // Affichage d'un point dans la brosse - void Pixel_figure_Dans_brosse(word Pos_X,word Pos_Y,byte Couleur) - { - Pos_X-=Brosse_Decalage_X; - Pos_Y-=Brosse_Decalage_Y; - if ( (Pos_XLimite_Bas) - Fin_Y=Limite_Bas; - if (Debut_XLimite_Droite) - Fin_X=Limite_Droite; - - // Affichage du cercle - for (Pos_Y=Debut_Y,Cercle_Curseur_Y=(long)Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Cercle_Curseur_Y++) - for (Pos_X=Debut_X,Cercle_Curseur_X=(long)Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Cercle_Curseur_X++) - if (Pixel_dans_cercle()) - Afficher_pixel(Pos_X,Pos_Y,Couleur); - - Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X+1-Debut_X,Fin_Y+1-Debut_Y); -} - - - // -- Tracer général d'une ellipse vide ----------------------------------- - -void Tracer_ellipse_vide_General(short Centre_X,short Centre_Y,short Rayon_horizontal,short Rayon_vertical,byte Couleur) -{ - short Debut_X; - short Debut_Y; - short Pos_X; - short Pos_Y; - - Debut_X=Centre_X-Rayon_horizontal; - Debut_Y=Centre_Y-Rayon_vertical; - - // Calcul des limites de l'ellipse - Ellipse_Calculer_limites(Rayon_horizontal+1,Rayon_vertical+1); - - // Affichage des extremitées de l'ellipse sur chaque quart de l'ellipse: - for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=-Rayon_vertical;Pos_YLimite_Bas) - Fin_Y=Limite_Bas; - if (Debut_XLimite_Droite) - Fin_X=Limite_Droite; - - // Affichage de l'ellipse - for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Ellipse_Curseur_Y++) - for (Pos_X=Debut_X,Ellipse_Curseur_X=Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Ellipse_Curseur_X++) - if (Pixel_dans_ellipse()) - Afficher_pixel(Pos_X,Pos_Y,Couleur); - Mettre_Ecran_A_Jour(Centre_X-Rayon_horizontal,Centre_Y-Rayon_vertical,2*Rayon_horizontal+1,2*Rayon_vertical+1); -} - -/****************** -* TRACÉ DE LIGNES * -******************/ - -void Rectifier_coordonnees_a_45_degres(short AX, short AY, short* BX, short* BY) -// Modifie BX et BY pour que la ligne AXAY - BXBY soit -// - une droite horizontale -// - une droite verticale -// - une droite avec une pente de 45 degrés -{ - int dx, dy; - float tan; - - dx = (*BX)-AX; - dy = AY- *BY; // On prend l'opposée car à l'écran les Y sont positifs en bas, et en maths, positifs en haut - - if (dx==0) return; // On est en lockx et de toutes façons le X n'a pas bougé, on sort tout de suite pour éviter une méchante division par 0 - - tan = (float)dy/(float)dx; - - if (tan <= 0.4142 && tan >= -0.4142) - { - // Cas 1 : Lock Y - *BY = AY; - } - else if ( tan > 0.4142 && tan < 2.4142) - { - // Cas 2 : dy=dx - int nBY = AY - dx; - *BY = (*BY + nBY)/2; - *BX = AX + AY - *BY; - } - else if (tan < -0.4142 && tan >= -2.4142) - { - // Cas 8 : dy = -dx - int nBY = AY + dx; - *BY = (*BY + nBY)/2; - *BX = AX - AY + *BY; - } - else - { - // Cas 3 : Lock X - *BX = AX; - } - - return; -} - - // -- Tracer général d'une ligne ------------------------------------------ - -void Tracer_ligne_General(short Debut_X,short Debut_Y,short Fin_X,short Fin_Y, byte Couleur) -{ - short Pos_X,Pos_Y; - short Incr_X,Incr_Y; - short i,Cumul; - short Delta_X,Delta_Y; - - - Pos_X=Debut_X; - Pos_Y=Debut_Y; - - if (Debut_XDelta_X) - { - Cumul=Delta_Y>>1; - for (i=1; i=Delta_Y) - { - Cumul-=Delta_Y; - Pos_X+=Incr_X; - } - Pixel_figure(Pos_X,Pos_Y,Couleur); - } - } - else - { - Cumul=Delta_X>>1; - for (i=1; i=Delta_X) - { - Cumul-=Delta_X; - Pos_Y+=Incr_Y; - } - Pixel_figure(Pos_X,Pos_Y,Couleur); - } - } - - if ( (Debut_X!=Fin_X) || (Debut_Y!=Fin_Y) ) - Pixel_figure(Fin_X,Fin_Y,Couleur); - -} - - // -- Tracer définitif d'une ligne -- - -void Tracer_ligne_Definitif(short Debut_X,short Debut_Y,short Fin_X,short Fin_Y, byte Couleur) -{ - - int L = Fin_X-Debut_X, H = Fin_Y - Debut_Y; - Pixel_figure=Pixel_figure_Definitif; - Tracer_ligne_General(Debut_X,Debut_Y,Fin_X,Fin_Y,Couleur); - Mettre_Ecran_A_Jour((Debut_XFin_X) - { - Tempo=Debut_X; - Debut_X=Fin_X; - Fin_X=Tempo; - } - if (Debut_Y>Fin_Y) - { - Tempo=Debut_Y; - Debut_Y=Fin_Y; - Fin_Y=Tempo; - } - - // On trace le rectangle: - - for (Pos_X=Debut_X;Pos_X<=Fin_X;Pos_X++) - Afficher_pinceau(Pos_X,Debut_Y,Couleur,0); - - for (Pos_Y=Debut_Y+1;Pos_YFin_X) - { - Tempo=Debut_X; - Debut_X=Fin_X; - Fin_X=Tempo; - } - if (Debut_Y>Fin_Y) - { - Tempo=Debut_Y; - Debut_Y=Fin_Y; - Fin_Y=Tempo; - } - - // Correction en cas de dépassement des limites de l'image - if (Fin_X>Limite_Droite) - Fin_X=Limite_Droite; - if (Fin_Y>Limite_Bas) - Fin_Y=Limite_Bas; - - // On trace le rectangle: - for (Pos_Y=Debut_Y;Pos_Y<=Fin_Y;Pos_Y++) - for (Pos_X=Debut_X;Pos_X<=Fin_X;Pos_X++) - // Afficher_pixel traite chaque pixel avec tous les effets ! (smear, ...) - // Donc on ne peut pas otimiser en traçant ligne par ligne avec memset :( - Afficher_pixel(Pos_X,Pos_Y,Couleur); - Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X-Debut_X,Fin_Y-Debut_Y); - -} - - - - - // -- Tracer une courbe de Bézier -- - -void Tracer_courbe_General(short X1, short Y1, - short X2, short Y2, - short X3, short Y3, - short X4, short Y4, - byte Couleur) -{ - float Delta,T,T2,T3; - short X,Y,Old_X,Old_Y; - word i; - int CX[4]; - int CY[4]; - - // Calcul des vecteurs de coefficients - CX[0]= - X1 + 3*X2 - 3*X3 + X4; - CX[1]= + 3*X1 - 6*X2 + 3*X3; - CX[2]= - 3*X1 + 3*X2; - CX[3]= + X1; - CY[0]= - Y1 + 3*Y2 - 3*Y3 + Y4; - CY[1]= + 3*Y1 - 6*Y2 + 3*Y3; - CY[2]= - 3*Y1 + 3*Y2; - CY[3]= + Y1; - - // Traçage de la courbe - Old_X=X1; - Old_Y=Y1; - Pixel_figure(Old_X,Old_Y,Couleur); - Delta=0.05; // 1.0/20 - T=0; - for (i=1; i<=20; i++) - { - T=T+Delta; T2=T*T; T3=T2*T; - X=Round(T3*CX[0] + T2*CX[1] + T*CX[2] + CX[3]); - Y=Round(T3*CY[0] + T2*CY[1] + T*CY[2] + CY[3]); - Tracer_ligne_General(Old_X,Old_Y,X,Y,Couleur); - Old_X=X; - Old_Y=Y; - } - - X = Min(Min(X1,X2),Min(X3,X4)); - Y = Min(Min(Y1,Y2),Min(Y3,Y4)); - Old_X = Max(Max(X1,X2),Max(X3,X4)) - X; - Old_Y = Max(Max(Y1,Y2),Max(Y3,Y4)) - Y; - Mettre_Ecran_A_Jour(X,Y,Old_X+1,Old_Y+1); -} - - // -- Tracer une courbe de Bézier définitivement -- - -void Tracer_courbe_Definitif(short X1, short Y1, - short X2, short Y2, - short X3, short Y3, - short X4, short Y4, - byte Couleur) -{ - Pixel_figure=Pixel_figure_Definitif; - Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); -} - - // -- Tracer la preview d'une courbe de Bézier -- - -void Tracer_courbe_Preview(short X1, short Y1, - short X2, short Y2, - short X3, short Y3, - short X4, short Y4, - byte Couleur) -{ - Pixel_figure=Pixel_figure_Preview; - Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); -} - - // -- Effacer la preview d'une courbe de Bézier -- - -void Effacer_courbe_Preview(short X1, short Y1, - short X2, short Y2, - short X3, short Y3, - short X4, short Y4, - byte Couleur) -{ - Pixel_figure=Pixel_figure_Effacer_preview; - Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); -} - - - - - // -- Spray : un petit coup de Pschiitt! -- - -void Aerographe(short Bouton_clicke) -{ - short Pos_X,Pos_Y; - short Rayon=Spray_Size>>1; - long Rayon_au_carre=(long)Rayon*Rayon; - short Indice,Count; - byte Indice_couleur; - byte Sens; - - - Effacer_curseur(); - - if (Spray_Mode) - { - for (Count=1; Count<=Spray_Mono_flow; Count++) - { - Pos_X=(rand()%Spray_Size)-Rayon; - Pos_Y=(rand()%Spray_Size)-Rayon; - if ( (Pos_X*Pos_X)+(Pos_Y*Pos_Y) <= Rayon_au_carre ) - { - Pos_X+=Pinceau_X; - Pos_Y+=Pinceau_Y; - if (Bouton_clicke==1) - Afficher_pinceau(Pos_X,Pos_Y,Fore_color,0); - else - Afficher_pinceau(Pos_X,Pos_Y,Back_color,0); - } - } - } - else - { - // On essaye de se balader dans la table des flux de façon à ce que ce - // ne soit pas toujours la dernière couleur qui soit affichée en dernier - // Pour ça, on part d'une couleur au pif dans une direction aléatoire. - Sens=rand()&1; - for (Indice=0,Indice_couleur=rand()/*%256*/; Indice<256; Indice++) - { - for (Count=1; Count<=Spray_Multi_flow[Indice_couleur]; Count++) - { - Pos_X=(rand()%Spray_Size)-Rayon; - Pos_Y=(rand()%Spray_Size)-Rayon; - if ( (Pos_X*Pos_X)+(Pos_Y*Pos_Y) <= Rayon_au_carre ) - { - Pos_X+=Pinceau_X; - Pos_Y+=Pinceau_Y; - if (Bouton_clicke==A_GAUCHE) - Afficher_pinceau(Pos_X,Pos_Y,Indice_couleur,0); - else - Afficher_pinceau(Pos_X,Pos_Y,Back_color,0); - } - } - if (Sens) - Indice_couleur++; - else - Indice_couleur--; - } - } - - Afficher_curseur(); - - for (Count=1; Count<=Spray_Delay; Count++) - Wait_VBL(); -} - - - - ////////////////////////////////////////////////////////////////////////// - ////////////////////////// GESTION DES DEGRADES ////////////////////////// - ////////////////////////////////////////////////////////////////////////// - - - // -- Gestion d'un dégradé de base (le plus moche) -- - -void Degrade_de_base(long Indice,short Pos_X,short Pos_Y) -{ - long Position; - - // On fait un premier calcul partiel - Position=(Indice*Degrade_Intervalle_bornes); - - // On gère un déplacement au hasard - Position+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; - Position-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; - - Position/=Degrade_Intervalle_total; - - // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors - // des valeurs autorisées par le dégradé défini par l'utilisateur. - - if (Position<0) - Position=0; - else if (Position>=Degrade_Intervalle_bornes) - Position=Degrade_Intervalle_bornes-1; - - // On ramène ensuite la position dans le dégradé vers un numéro de couleur - if (Degrade_Inverse) - Traiter_pixel_de_degrade(Pos_X,Pos_Y,Degrade_Borne_Superieure-Position); - else - Traiter_pixel_de_degrade(Pos_X,Pos_Y,Degrade_Borne_Inferieure+Position); -} - - - // -- Gestion d'un dégradé par trames simples -- - -void Degrade_de_trames_simples(long Indice,short Pos_X,short Pos_Y) -{ - long Position_dans_degrade; - long Position_dans_segment; - - // - // But de l'opération: en plus de calculer la position de base (désignée - // dans cette procédure par "Position_dans_degrade", on calcule la position - // de l'indice dans le schéma suivant: - // - // | Les indices qui traînent de ce côté du segment se voient subir - // | une incrémentation conditionnelle à leur position dans l'écran. - // v - // |---|---|---|---- - - - - // ^ - // |_ Les indices qui traînent de ce côté du segment se voient subir une - // décrémentation conditionnelle à leur position dans l'écran. - - // On fait d'abord un premier calcul partiel - Position_dans_degrade=(Indice*Degrade_Intervalle_bornes); - - // On gère un déplacement au hasard... - Position_dans_degrade+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; - Position_dans_degrade-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; - - if (Position_dans_degrade<0) - Position_dans_degrade=0; - - // ... qui nous permet de calculer la position dans le segment - Position_dans_segment=((Position_dans_degrade<<2)/Degrade_Intervalle_total)&3; - - // On peut ensuite terminer le calcul de l'indice dans le dégradé - Position_dans_degrade/=Degrade_Intervalle_total; - - // On va pouvoir discuter de la valeur de Position_dans_degrade en fonction - // de la position dans l'écran et de la Position_dans_segment. - - switch (Position_dans_segment) - { - case 0 : // On est sur la gauche du segment - if (((Pos_X+Pos_Y)&1)==0) - Position_dans_degrade--; - break; - - // On n'a pas à traiter les cas 1 et 2 car ils représentent des valeurs - // suffisament au centre du segment pour ne pas avoir à subir la trame - - case 3 : // On est sur la droite du segment - if (((Pos_X+Pos_Y)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame. - Position_dans_degrade++; - } - - // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors - // des valeurs autorisées par le dégradé défini par l'utilisateur. - - if (Position_dans_degrade<0) - Position_dans_degrade=0; - else if (Position_dans_degrade>=Degrade_Intervalle_bornes) - Position_dans_degrade=Degrade_Intervalle_bornes-1; - - // On ramène ensuite la position dans le dégradé vers un numéro de couleur - if (Degrade_Inverse) - Position_dans_degrade=Degrade_Borne_Superieure-Position_dans_degrade; - else - Position_dans_degrade=Degrade_Borne_Inferieure+Position_dans_degrade; - - Traiter_pixel_de_degrade(Pos_X,Pos_Y,Position_dans_degrade); -} - - - // -- Gestion d'un dégradé par trames étendues -- - -void Degrade_de_trames_etendues(long Indice,short Pos_X,short Pos_Y) -{ - long Position_dans_degrade; - long Position_dans_segment; - -// - // But de l'opération: en plus de calculer la position de base (désignée - // dans cette procédure par "Position_dans_degrade", on calcule la position - // de l'indice dans le schéma suivant: - // - // | Les indices qui traînent de ce côté du segment se voient subir - // | une incrémentation conditionnelle à leur position dans l'écran. - // v - // |---|---|---|---- - - - - // ^ - // |_ Les indices qui traînent de ce côté du segment se voient subir une - // décrémentation conditionnelle à leur position dans l'écran. - - // On fait d'abord un premier calcul partiel - Position_dans_degrade=(Indice*Degrade_Intervalle_bornes); - - // On gère un déplacement au hasard - Position_dans_degrade+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; - Position_dans_degrade-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; - - if (Position_dans_degrade<0) - Position_dans_degrade=0; - - // Qui nous permet de calculer la position dans le segment - Position_dans_segment=((Position_dans_degrade<<3)/Degrade_Intervalle_total)&7; - - // On peut ensuite terminer le calcul de l'indice dans le dégradé - Position_dans_degrade/=Degrade_Intervalle_total; - - // On va pouvoir discuter de la valeur de Position_dans_degrade en fonction - // de la position dans l'écran et de la Position_dans_segment. - - switch (Position_dans_segment) - { - case 0 : // On est sur l'extrême gauche du segment - if (((Pos_X+Pos_Y)&1)==0) - Position_dans_degrade--; - break; - - case 1 : // On est sur la gauche du segment - case 2 : // On est sur la gauche du segment - if (((Pos_X & 1)==0) && ((Pos_Y & 1)==0)) - Position_dans_degrade--; - break; - - // On n'a pas à traiter les cas 3 et 4 car ils représentent des valeurs - // suffisament au centre du segment pour ne pas avoir à subir la trame - - case 5 : // On est sur la droite du segment - case 6 : // On est sur la droite du segment - if (((Pos_X & 1)==0) && ((Pos_Y & 1)!=0)) - Position_dans_degrade++; - break; - - case 7 : // On est sur l'extreme droite du segment - if (((Pos_X+Pos_Y)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame. - Position_dans_degrade++; - } - - // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors - // des valeurs autorisées par le dégradé défini par l'utilisateur. - - if (Position_dans_degrade<0) - Position_dans_degrade=0; - else if (Position_dans_degrade>=Degrade_Intervalle_bornes) - Position_dans_degrade=Degrade_Intervalle_bornes-1; - - // On ramène ensuite la position dans le dégradé vers un numéro de couleur - if (Degrade_Inverse) - Position_dans_degrade=Degrade_Borne_Superieure-Position_dans_degrade; - else - Position_dans_degrade=Degrade_Borne_Inferieure+Position_dans_degrade; - - Traiter_pixel_de_degrade(Pos_X,Pos_Y,Position_dans_degrade); -} - - - - // -- Tracer un cercle degradé (une sphère) -- - -void Tracer_cercle_degrade(short Centre_X,short Centre_Y,short Rayon,short Eclairage_X,short Eclairage_Y) -{ - long Debut_X; - long Debut_Y; - long Pos_X; - long Pos_Y; - long Fin_X; - long Fin_Y; - long Distance_X; // Distance (au carré) sur les X du point en cours au centre d'éclairage - long Distance_Y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage - - Debut_X=Centre_X-Rayon; - Debut_Y=Centre_Y-Rayon; - Fin_X=Centre_X+Rayon; - Fin_Y=Centre_Y+Rayon; - - // Correction des bornes d'après les limites - if (Debut_YLimite_Bas) - Fin_Y=Limite_Bas; - if (Debut_XLimite_Droite) - Fin_X=Limite_Droite; - - Degrade_Intervalle_total=Cercle_Limite+ - ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ - ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y))+ - (2L*Rayon*sqrt( - ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ - ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y)))); - - if (Degrade_Intervalle_total==0) - Degrade_Intervalle_total=1; - - // Affichage du cercle - for (Pos_Y=Debut_Y,Cercle_Curseur_Y=(long)Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Cercle_Curseur_Y++) - { - Distance_Y =(Pos_Y-Eclairage_Y); - Distance_Y*=Distance_Y; - for (Pos_X=Debut_X,Cercle_Curseur_X=(long)Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Cercle_Curseur_X++) - if (Pixel_dans_cercle()) - { - Distance_X =(Pos_X-Eclairage_X); - Distance_X*=Distance_X; - Traiter_degrade(Distance_X+Distance_Y,Pos_X,Pos_Y); - } - } - - Mettre_Ecran_A_Jour(Centre_X-Rayon,Centre_Y-Rayon,2*Rayon+1,2*Rayon+1); -} - - - // -- Tracer une ellipse degradée -- - -void Tracer_ellipse_degradee(short Centre_X,short Centre_Y,short Rayon_horizontal,short Rayon_vertical,short Eclairage_X,short Eclairage_Y) -{ - long Debut_X; - long Debut_Y; - long Pos_X; - long Pos_Y; - long Fin_X; - long Fin_Y; - long Distance_X; // Distance (au carré) sur les X du point en cours au centre d'éclairage - long Distance_Y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage - - - Debut_X=Centre_X-Rayon_horizontal; - Debut_Y=Centre_Y-Rayon_vertical; - Fin_X=Centre_X+Rayon_horizontal; - Fin_Y=Centre_Y+Rayon_vertical; - - // Calcul des limites de l'ellipse - Ellipse_Calculer_limites(Rayon_horizontal+1,Rayon_vertical+1); - - // On calcule la distance maximale: - Degrade_Intervalle_total=(Rayon_horizontal*Rayon_horizontal)+ - (Rayon_vertical*Rayon_vertical)+ - ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ - ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y))+ - (2L - *sqrt( - (Rayon_horizontal*Rayon_horizontal)+ - (Rayon_vertical *Rayon_vertical )) - *sqrt( - ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ - ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y)))); - - if (Degrade_Intervalle_total==0) - Degrade_Intervalle_total=1; - - // Correction des bornes d'après les limites - if (Debut_YLimite_Bas) - Fin_Y=Limite_Bas; - if (Debut_XLimite_Droite) - Fin_X=Limite_Droite; - - // Affichage de l'ellipse - for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Ellipse_Curseur_Y++) - { - Distance_Y =(Pos_Y-Eclairage_Y); - Distance_Y*=Distance_Y; - for (Pos_X=Debut_X,Ellipse_Curseur_X=Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Ellipse_Curseur_X++) - if (Pixel_dans_ellipse()) - { - Distance_X =(Pos_X-Eclairage_X); - Distance_X*=Distance_X; - Traiter_degrade(Distance_X+Distance_Y,Pos_X,Pos_Y); - } - } - - Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X-Debut_X+1,Fin_Y-Debut_Y+1); -} - - -// Tracé d'un rectangle (RAX RAY - RBX RBY) dégradé selon le vecteur (VAX VAY - VBX - VBY) -void Tracer_rectangle_degrade(short RAX,short RAY,short RBX,short RBY,short VAX,short VAY, short VBX, short VBY) -{ - short Pos_Y, Pos_X; - - // On commence par s'assurer que le rectangle est à l'endroit - if(RBX < RAX) - { - Pos_X = RBX; - RBX = RAX; - RAX = Pos_X; - } - - if(RBY < RAY) - { - Pos_Y = RBY; - RBY = RAY; - RAY = Pos_Y; - } - - // Correction des bornes d'après les limites - if (RAYLimite_Bas) - RBY=Limite_Bas; - if (RAXLimite_Droite) - RBX=Limite_Droite; - - if(VBX == VAX) - { - // Le vecteur est vertical, donc on évite la partie en dessous qui foirerait avec une division par 0... - if (VBY == VAY) return; // L'utilisateur fait n'importe quoi - Degrade_Intervalle_total = abs(VBY - VAY); - for(Pos_Y=RAY;Pos_Y<=RBY;Pos_Y++) - for(Pos_X=RAX;Pos_X<=RBX;Pos_X++) - Traiter_degrade(abs(VBY - Pos_Y),Pos_X,Pos_Y); - - } - else - { - float a; - float b; - float Distance_X, Distance_Y; - - Degrade_Intervalle_total = sqrt(pow(VBY - VAY,2)+pow(VBX - VAX,2)); - a = (float)(VBY - VAY)/(float)(VBX - VAX); - b = VAY - a*VAX; - - for (Pos_Y=RAY;Pos_Y<=RBY;Pos_Y++) - for (Pos_X = RAX;Pos_X<=RBX;Pos_X++) - { - // On calcule ou on en est dans le dégradé - Distance_X = pow((Pos_Y - VAY),2)+pow((Pos_X - VAX),2); - Distance_Y = pow((-a * Pos_X + Pos_Y - b),2)/(a*a+1); - - Traiter_degrade((int)sqrt(Distance_X - Distance_Y),Pos_X,Pos_Y); - } - } - Mettre_Ecran_A_Jour(RAX,RAY,RBX,RBY); -} - - - - -// -- Tracer un polygône plein -- - -typedef struct POLYGON_EDGE /* an active edge */ -{ - short top; /* top y position */ - short bottom; /* bottom y position */ - float x, dx; /* floating point x position and gradient */ - float w; /* width of line segment */ - struct POLYGON_EDGE *prev; /* doubly linked list */ - struct POLYGON_EDGE *next; -} POLYGON_EDGE; - - - -/* fill_edge_structure: - * Polygon helper function: initialises an edge structure for the 2d - * rasteriser. - */ -void fill_edge_structure(POLYGON_EDGE *edge, short *i1, short *i2) -{ - short *it; - - if (i2[1] < i1[1]) - { - it = i1; - i1 = i2; - i2 = it; - } - - edge->top = i1[1]; - edge->bottom = i2[1] - 1; - edge->dx = ((float) i2[0] - (float) i1[0]) / ((float) i2[1] - (float) i1[1]); - edge->x = i1[0] + 0.4999999; - edge->prev = NULL; - edge->next = NULL; - - if (edge->dx+1 < 0.0) - edge->x += edge->dx+1; - - if (edge->dx >= 0.0) - edge->w = edge->dx; - else - edge->w = -(edge->dx); - - if (edge->w-1.0<0.0) - edge->w = 0.0; - else - edge->w = edge->w-1; -} - - - -/* add_edge: - * Adds an edge structure to a linked list, returning the new head pointer. - */ -POLYGON_EDGE * add_edge(POLYGON_EDGE *list, POLYGON_EDGE *edge, int sort_by_x) -{ - POLYGON_EDGE *pos = list; - POLYGON_EDGE *prev = NULL; - - if (sort_by_x) - { - while ( (pos) && ((pos->x+((pos->w+pos->dx)/2)) < (edge->x+((edge->w+edge->dx)/2))) ) - { - prev = pos; - pos = pos->next; - } - } - else - { - while ((pos) && (pos->top < edge->top)) - { - prev = pos; - pos = pos->next; - } - } - - edge->next = pos; - edge->prev = prev; - - if (pos) - pos->prev = edge; - - if (prev) - { - prev->next = edge; - return list; - } - else - return edge; -} - - - -/* remove_edge: - * Removes an edge structure from a list, returning the new head pointer. - */ -POLYGON_EDGE * remove_edge(POLYGON_EDGE *list, POLYGON_EDGE *edge) -{ - if (edge->next) - edge->next->prev = edge->prev; - - if (edge->prev) - { - edge->prev->next = edge->next; - return list; - } - else - return edge->next; -} - - - -/* polygon: - * Draws a filled polygon with an arbitrary number of corners. Pass the - * number of vertices, then an array containing a series of x, y points - * (a total of vertices*2 values). - */ -void Polyfill_General(int Vertices, short * Points, int Color) -{ - short c; - short top = 0x7FFF; - short bottom = 0; - short *i1, *i2; - short Pos_X,Fin_X; - POLYGON_EDGE *edge, *next_edge, *initial_edge; - POLYGON_EDGE *active_edges = NULL; - POLYGON_EDGE *inactive_edges = NULL; - - - /* allocate some space and fill the edge table */ - initial_edge=edge=(POLYGON_EDGE *) malloc(sizeof(POLYGON_EDGE) * Vertices); - - i1 = Points; - i2 = Points + ((Vertices-1)<<1); - - for (c=0; cbottom >= edge->top) - { - if (edge->top < top) - top = edge->top; - - if (edge->bottom > bottom) - bottom = edge->bottom; - - inactive_edges = add_edge(inactive_edges, edge, 0); - edge++; - } - } - i2 = i1; - i1 += 2; - } - - /* for each scanline in the polygon... */ - for (c=top; c<=bottom; c++) - { - /* check for newly active edges */ - edge = inactive_edges; - while ((edge) && (edge->top == c)) - { - next_edge = edge->next; - inactive_edges = remove_edge(inactive_edges, edge); - active_edges = add_edge(active_edges, edge, 1); - edge = next_edge; - } - - /* draw horizontal line segments */ - if ((c>=Limite_Haut) && (c<=Limite_Bas)) - { - edge = active_edges; - while ((edge) && (edge->next)) - { - Pos_X=/*Round*/(edge->x); - Fin_X=/*Round*/(edge->next->x+edge->next->w); - if (Pos_XLimite_Droite) - Fin_X=Limite_Droite; - for (; Pos_X<=Fin_X; Pos_X++) - Pixel_figure(Pos_X,c,Color); - edge = edge->next->next; - } - } - - /* update edges, sorting and removing dead ones */ - edge = active_edges; - while (edge) - { - next_edge = edge->next; - if (c >= edge->bottom) - active_edges = remove_edge(active_edges, edge); - else - { - edge->x += edge->dx; - while ((edge->prev) && ( (edge->x+(edge->w/2)) < (edge->prev->x+(edge->prev->w/2))) ) - { - if (edge->next) - edge->next->prev = edge->prev; - edge->prev->next = edge->next; - edge->next = edge->prev; - edge->prev = edge->prev->prev; - edge->next->prev = edge; - if (edge->prev) - edge->prev->next = edge; - else - active_edges = edge; - } - } - edge = next_edge; - } - } - - free(initial_edge); - // On ne connait pas simplement les xmin et xmax ici, mais de toutes façon ce n'est pas utilisé en preview - Mettre_Ecran_A_Jour(0,top,Principal_Largeur_image,bottom-top+1); -} - - -void Polyfill(int Vertices, short * Points, int Color) -{ - Pixel_figure=Afficher_pixel; - Polyfill_General(Vertices,Points,Color); -} - - - -//------------ Remplacement de la couleur pointée par une autre -------------- - -void Remplacer(byte Nouvelle_couleur) -{ - byte Ancienne_couleur; - - if ((Pinceau_X255)) - Indice++; - - // On note la position de la première case de la séquence - Premier=Indice; - - // On recherche la position de la dernière case de la séquence - for (Dernier=Premier;Liste[Dernier+1]<256;Dernier++); - - // Pour toutes les cases non vides (et non inhibées) qui suivent - switch (Mode) - { - case MODE_SHADE_NORMAL : - for (;(Indice<512) && (Liste[Indice]<256);Indice++) - { // On met à jour les tables de conversion - Couleur=Liste[Indice]; - Table_inc[Couleur]=Liste[(Indice+Pas<=Dernier)?Indice+Pas:Dernier]; - Table_dec[Couleur]=Liste[(Indice-Pas>=Premier)?Indice-Pas:Premier]; - } - break; - case MODE_SHADE_BOUCLE : - Temp=1+Dernier-Premier; - for (;(Indice<512) && (Liste[Indice]<256);Indice++) - { // On met à jour les tables de conversion - Couleur=Liste[Indice]; - Table_inc[Couleur]=Liste[Premier+((Pas+Indice-Premier)%Temp)]; - Table_dec[Couleur]=Liste[Premier+(((Temp-Pas)+Indice-Premier)%Temp)]; - } - break; - default : // MODE_SHADE_NOSAT - for (;(Indice<512) && (Liste[Indice]<256);Indice++) - { // On met à jour les tables de conversion - Couleur=Liste[Indice]; - if (Indice+Pas<=Dernier) - Table_inc[Couleur]=Liste[Indice+Pas]; - if (Indice-Pas>=Premier) - Table_dec[Couleur]=Liste[Indice-Pas]; - } - } - } -} - - - -// -- Interface avec l'image, affectée par le facteur de grossissement ------- - - // fonction d'affichage "Pixel" utilisée pour les opérations définitivement - // Ne doit à aucune condition être appelée en dehors de la partie visible - // de l'image dans l'écran (ça pourrait être grave) -void Afficher_pixel(word X,word Y,byte Couleur) - // X & Y sont la position d'un point dans l'IMAGE - // Couleur est la couleur du point - // Le Stencil est géré. - // Les effets sont gérés par appel à Fonction_effet(). - // La Loupe est gérée par appel à Pixel_Preview(). -{ - if ( ( (!Trame_Mode) || (Effet_Trame(X,Y)) ) - && (!((Stencil_Mode) && (Stencil[Lit_pixel_dans_ecran_courant(X,Y)]))) - && (!((Mask_Mode) && (Mask_table[Lit_pixel_dans_ecran_brouillon(X,Y)]))) ) - { - Couleur=Fonction_effet(X,Y,Couleur); - Pixel_dans_ecran_courant(X,Y,Couleur); - Pixel_Preview(X,Y,Couleur); - } -} - - - -// -- Calcul des différents effets ------------------------------------------- - - // -- Aucun effet en cours -- - -byte Aucun_effet(__attribute__((unused)) word X,__attribute__((unused)) word Y,byte Couleur) -{ - return Couleur; -} - - // -- Effet de Shading -- - -byte Effet_Shade(word X,word Y,__attribute__((unused)) byte Couleur) -{ - return Shade_Table[Lit_pixel_dans_ecran_feedback(X,Y)]; -} - -byte Effet_Quick_shade(word X,word Y,byte Couleur) -{ - int C=Couleur=Lit_pixel_dans_ecran_feedback(X,Y); - int Sens=(Fore_color<=Back_color); - byte Debut,Fin; - int Largeur; - - if (Sens) - { - Debut=Fore_color; - Fin =Back_color; - } - else - { - Debut=Back_color; - Fin =Fore_color; - } - - if ((C>=Debut) && (C<=Fin) && (Debut!=Fin)) - { - Largeur=1+Fin-Debut; - - if ( ((Mouse_K==A_GAUCHE) && Sens) || ((Mouse_K==A_DROITE) && (!Sens)) ) - C-=Quick_shade_Step%Largeur; - else - C+=Quick_shade_Step%Largeur; - - if (CFin) - switch (Quick_shade_Loop) - { - case MODE_SHADE_NORMAL : return Fin; - case MODE_SHADE_BOUCLE : return (C-Largeur); - default : return Couleur; - } - } - - return C; -} - - // -- Effet de Tiling -- - -byte Effet_Tiling(word X,word Y,__attribute__((unused)) byte Couleur) -{ - return Lit_pixel_dans_brosse((X+Brosse_Largeur-Tiling_Decalage_X)%Brosse_Largeur, - (Y+Brosse_Hauteur-Tiling_Decalage_Y)%Brosse_Hauteur); -} - - // -- Effet de Smooth -- - -byte Effet_Smooth(word X,word Y,__attribute__((unused)) byte Couleur) -{ - int R,V,B; - byte C; - int Poids,Poids_total; - byte X2=((X+1) Hauteur_ecran/200) + + Facteur=Hauteur_ecran/200; + + else + + Facteur=Largeur_ecran/320; + + + + switch (Config.Ratio) + + { + + case 1: // adapter tout + + Menu_Facteur_X=Facteur; + + Menu_Facteur_Y=Facteur; + + break; + + case 2: // adapter légèrement + + Menu_Facteur_X=Facteur-1; + + if (Menu_Facteur_X<1) Menu_Facteur_X=1; + + Menu_Facteur_Y=Facteur-1; + + if (Menu_Facteur_Y<1) Menu_Facteur_Y=1; + + break; + + default: // ne pas adapter + + Menu_Facteur_X=1; + + Menu_Facteur_Y=1; + + } + + if (Pixel_height>Pixel_width) + + Menu_Facteur_X*=2; + + else if (Pixel_width>Pixel_height) + + Menu_Facteur_Y*=2; + + if (Buffer_de_ligne_horizontale) + + free(Buffer_de_ligne_horizontale); + + + + Buffer_de_ligne_horizontale=(byte *)malloc(Pixel_width*((Largeur_ecran>Principal_Largeur_image)?Largeur_ecran:Principal_Largeur_image)); + + + + Set_palette(Principal_Palette); + + + + if (!Fullscreen) + + Resolution_actuelle=0; + + else + + for (Indice=1; Indice> 3; + + Menu_Ordonnee = Hauteur_ecran; + + if (Menu_visible) + + Menu_Ordonnee -= HAUTEUR_MENU * Menu_Facteur_Y; + + Menu_Ordonnee_Texte = Hauteur_ecran-(Menu_Facteur_Y<<3); + + Bouton[BOUTON_CHOIX_COL].Largeur=(Menu_Taille_couleur<<3)-1; + + + + Sensibilite_X = Config.Indice_Sensibilite_souris_X; + + Sensibilite_Y = Config.Indice_Sensibilite_souris_Y; + + Sensibilite_X>>=Mouse_Facteur_de_correction_X; + + Sensibilite_Y>>=Mouse_Facteur_de_correction_Y; + + Sensibilite_souris(Sensibilite_X?Sensibilite_X:1,Sensibilite_Y?Sensibilite_Y:1); + + + + Brouillon_Decalage_X=0; // | Il faut penser à éviter les incohérences + + Brouillon_Decalage_Y=0; // |- de décalage du brouillon par rapport à + + Brouillon_Loupe_Mode=0; // | la résolution. + + } + + if (Loupe_Mode) + + { + + Pixel_Preview=Pixel_Preview_Loupe; + + } + + else + + { + + Pixel_Preview=Pixel_Preview_Normal; + + // Recaler la vue (meme clipping que dans Scroller_ecran()) + + if (Principal_Decalage_X+Largeur_ecran>Principal_Largeur_image) + + Principal_Decalage_X=Principal_Largeur_image-Largeur_ecran; + + if (Principal_Decalage_X<0) + + Principal_Decalage_X=0; + + if (Principal_Decalage_Y+Menu_Ordonnee>Principal_Hauteur_image) + + Principal_Decalage_Y=Principal_Hauteur_image-Menu_Ordonnee; + + if (Principal_Decalage_Y<0) + + Principal_Decalage_Y=0; + + } + + + + Calculer_donnees_loupe(); + + if (Loupe_Mode) + + Recadrer_ecran_par_rapport_au_zoom(); + + Calculer_limites(); + + Calculer_coordonnees_pinceau(); + + Afficher_ecran(); + + + + Resize_Largeur=0; + + Resize_Hauteur=0; + +} + + + + + + + + // -- Redimentionner l'image (nettoie l'écran virtuel) -- + + + +void Redimentionner_image(word Largeur_choisie,word Hauteur_choisie) + +{ + + word Ancienne_largeur=Principal_Largeur_image; + + word Ancienne_hauteur=Principal_Hauteur_image; + + + + // +-+-+ + + // |C| | A+B+C = Ancienne image + + // +-+A| + + // |B| | C = Nouvelle image + + // +-+-+ + + + + if (Backup_avec_nouvelles_dimensions(1,Largeur_choisie,Hauteur_choisie)) + + { + + // La nouvelle page a pu être allouée, elle est pour l'instant pleine de + + // 0s. Elle fait Principal_Largeur_image de large. + + + + // On copie donc maintenant la partie C dans la nouvelle image. + + Copier_une_partie_d_image_dans_une_autre( + + Ecran_backup,0,0,Min(Ancienne_largeur,Principal_Largeur_image), + + Min(Ancienne_hauteur,Principal_Hauteur_image),Ancienne_largeur, + + Principal_Ecran,0,0,Principal_Largeur_image); + + } + + else + + { + + // Afficher un message d'erreur + + Afficher_curseur(); + + Message_Memoire_insuffisante(); + + Effacer_curseur(); + + } + +} + + + + + + + +void Remap_picture(void) + +{ + + short Pos_X; // Variable de balayage de la brosse + + short Pos_Y; // Variable de balayage de la brosse + + byte Utilisee[256]; // Tableau de booléens "La couleur est utilisée" + + int Couleur; + + + + // On commence par initialiser le tableau de booléens à faux + + for (Couleur=0;Couleur<=255;Couleur++) + + Utilisee[Couleur]=0; + + + + // On calcule la table d'utilisation des couleurs + + for (Pos_Y=0;Pos_YLimite_Gauche) && + + (Lit_pixel_dans_ecran_courant(Debut_X-1,Ligne)==2)) || + + // Test de la présence d'un point à droite du segment + + ((Fin_X-1Limite_Haut)) + + for (Pos_X=Debut_X;Pos_X*Limite_atteinte_Droite) + + *Limite_atteinte_Droite=Fin_X; + + // On remplit le segment de Debut_X à Fin_X-1. + + for (Pos_X=Debut_X;Pos_XLimite_Haut) + + Limite_courante_Haut--; + + + + for (Ligne=Limite_courante_Bas;Ligne>=Limite_courante_Haut;Ligne--) + + { + + Ligne_modifiee=0; + + // On va traiter le cas de la ligne n° Ligne. + + + + // On commence le traitement à la gauche de l'écran + + Debut_X=Limite_Gauche; + + + + // Pour chaque segment de couleur 1 que peut contenir la ligne + + while (Debut_X<=Limite_Droite) + + { + + // On cherche son début + + for (;(Debut_X<=Limite_Droite) && + + (Lit_pixel_dans_ecran_courant(Debut_X,Ligne)!=1);Debut_X++); + + + + if (Debut_X<=Limite_Droite) + + { + + // Un segment de couleur 1 existe et commence à la position Debut_X. + + // On va donc en chercher la fin. + + for (Fin_X=Debut_X+1;(Fin_X<=Limite_Droite) && + + (Lit_pixel_dans_ecran_courant(Fin_X,Ligne)==1);Fin_X++); + + + + // On sait qu'il existe un segment de couleur 1 qui commence en + + // Debut_X et qui se termine en Fin_X-1. + + + + // On va maintenant regarder si une couleur sur la périphérie + + // permet de colorier ce segment avec la couleur 2. + + + + Propagation_possible=( + + // Test de la présence d'un point à gauche du segment + + ((Debut_X>Limite_Gauche) && + + (Lit_pixel_dans_ecran_courant(Debut_X-1,Ligne)==2)) || + + // Test de la présence d'un point à droite du segment + + ((Fin_X-1*Limite_atteinte_Droite) + + *Limite_atteinte_Droite=Fin_X; + + // On remplit le segment de Debut_X à Fin_X-1. + + for (Pos_X=Debut_X;Pos_XLimite_Haut) ) + + Limite_courante_Haut--; // On monte cette limite vers le haut + + } + + } + + + + *Limite_atteinte_Haut=Limite_courante_Haut; + + *Limite_atteinte_Bas =Limite_courante_Bas; + + (*Limite_atteinte_Droite)--; + +} // Fin de la routine de remplissage "Fill" + + + + + +void Remplir(byte Couleur_de_remplissage) + +// + +// Cette fonction fait un remplissage qui gère tous les effets. Elle fait + +// appel à "Fill()". + +// + +{ + + byte Forme_curseur_avant_remplissage; + + byte * FX_Feedback_Ecran_avant_remplissage; + + short Pos_X,Pos_Y; + + short Limite_atteinte_Haut ,Limite_atteinte_Bas; + + short Limite_atteinte_Gauche,Limite_atteinte_Droite; + + byte Table_de_remplacement[256]; + + + + + + // Avant toute chose, on vérifie que l'on n'est pas en train de remplir + + // en dehors de l'image: + + + + if ( (Pinceau_X>=Limite_Gauche) && + + (Pinceau_X<=Limite_Droite) && + + (Pinceau_Y>=Limite_Haut) && + + (Pinceau_Y<=Limite_Bas) ) + + { + + // On suppose que le curseur est déjà caché. + + // Effacer_curseur(); + + + + // On va faire patienter l'utilisateur en lui affichant un joli petit + + // sablier: + + Forme_curseur_avant_remplissage=Forme_curseur; + + Forme_curseur=FORME_CURSEUR_SABLIER; + + Afficher_curseur(); + + + + // On commence par effectuer un backup de l'image. + + Backup(); + + + + // On fait attention au Feedback qui DOIT se faire avec le backup. + + FX_Feedback_Ecran_avant_remplissage=FX_Feedback_Ecran; + + FX_Feedback_Ecran=Ecran_backup; + + + + // On va maintenant "épurer" la zone visible de l'image: + + memset(Table_de_remplacement,0,256); + + Table_de_remplacement[Lit_pixel_dans_ecran_courant(Pinceau_X,Pinceau_Y)]=1; + + Remplacer_toutes_les_couleurs_dans_limites(Table_de_remplacement); + + + + // On fait maintenant un remplissage classique de la couleur 1 avec la 2 + + Fill(&Limite_atteinte_Haut ,&Limite_atteinte_Bas, + + &Limite_atteinte_Gauche,&Limite_atteinte_Droite); + + + + // On s'apprête à faire des opérations qui nécessitent un affichage. Il + + // faut donc retirer de l'écran le curseur: + + Effacer_curseur(); + + Forme_curseur=Forme_curseur_avant_remplissage; + + + + // Il va maintenant falloir qu'on "turn" ce gros caca "into" un truc qui + + // ressemble un peu plus à ce à quoi l'utilisateur peut s'attendre. + + if (Limite_atteinte_Haut>Limite_Haut) + + Copier_une_partie_d_image_dans_une_autre(Ecran_backup, // Source + + Limite_Gauche,Limite_Haut, // Pos X et Y dans source + + (Limite_Droite-Limite_Gauche)+1, // Largeur copie + + Limite_atteinte_Haut-Limite_Haut,// Hauteur copie + + Principal_Largeur_image, // Largeur de la source + + Principal_Ecran, // Destination + + Limite_Gauche,Limite_Haut, // Pos X et Y destination + + Principal_Largeur_image); // Largeur destination + + if (Limite_atteinte_BasLimite_Gauche) + + Copier_une_partie_d_image_dans_une_autre(Ecran_backup, + + Limite_Gauche,Limite_atteinte_Haut, + + Limite_atteinte_Gauche-Limite_Gauche, + + (Limite_atteinte_Bas-Limite_atteinte_Haut)+1, + + Principal_Largeur_image,Principal_Ecran, + + Limite_Gauche,Limite_atteinte_Haut,Principal_Largeur_image); + + if (Limite_atteinte_Droite=Limite_Gauche) && + + (Pos_X<=Limite_Droite) && + + (Pos_Y>=Limite_Haut) && + + (Pos_Y<=Limite_Bas) ) + + Pixel_Preview(Pos_X,Pos_Y,Couleur); + + } + + // Affichage d'un point pour une preview, avec sa propre couleur + + void Pixel_figure_Preview_auto(word Pos_X,word Pos_Y) + + { + + if ( (Pos_X>=Limite_Gauche) && + + (Pos_X<=Limite_Droite) && + + (Pos_Y>=Limite_Haut) && + + (Pos_Y<=Limite_Bas) ) + + Pixel_Preview(Pos_X,Pos_Y,Lit_pixel_dans_ecran_courant(Pos_X,Pos_Y)); + + } + + + + // Affichage d'un point pour une preview en xor + + void Pixel_figure_Preview_xor(word Pos_X,word Pos_Y,__attribute__((unused)) byte Couleur) + + { + + if ( (Pos_X>=Limite_Gauche) && + + (Pos_X<=Limite_Droite) && + + (Pos_Y>=Limite_Haut) && + + (Pos_Y<=Limite_Bas) ) + + Pixel_Preview(Pos_X,Pos_Y,~Lit_pixel(Pos_X-Principal_Decalage_X, + + Pos_Y-Principal_Decalage_Y)); + + } + + + + // Effacement d'un point de preview + + void Pixel_figure_Effacer_preview(word Pos_X,word Pos_Y,__attribute__((unused)) byte Couleur) + + { + + if ( (Pos_X>=Limite_Gauche) && + + (Pos_X<=Limite_Droite) && + + (Pos_Y>=Limite_Haut) && + + (Pos_Y<=Limite_Bas) ) + + Pixel_Preview(Pos_X,Pos_Y,Lit_pixel_dans_ecran_courant(Pos_X,Pos_Y)); + + } + + + + // Affichage d'un point dans la brosse + + void Pixel_figure_Dans_brosse(word Pos_X,word Pos_Y,byte Couleur) + + { + + Pos_X-=Brosse_Decalage_X; + + Pos_Y-=Brosse_Decalage_Y; + + if ( (Pos_XLimite_Bas) + + Fin_Y=Limite_Bas; + + if (Debut_XLimite_Droite) + + Fin_X=Limite_Droite; + + + + // Affichage du cercle + + for (Pos_Y=Debut_Y,Cercle_Curseur_Y=(long)Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Cercle_Curseur_Y++) + + for (Pos_X=Debut_X,Cercle_Curseur_X=(long)Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Cercle_Curseur_X++) + + if (Pixel_dans_cercle()) + + Afficher_pixel(Pos_X,Pos_Y,Couleur); + + + + Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X+1-Debut_X,Fin_Y+1-Debut_Y); + +} + + + + + + // -- Tracer général d'une ellipse vide ----------------------------------- + + + +void Tracer_ellipse_vide_General(short Centre_X,short Centre_Y,short Rayon_horizontal,short Rayon_vertical,byte Couleur) + +{ + + short Debut_X; + + short Debut_Y; + + short Pos_X; + + short Pos_Y; + + + + Debut_X=Centre_X-Rayon_horizontal; + + Debut_Y=Centre_Y-Rayon_vertical; + + + + // Calcul des limites de l'ellipse + + Ellipse_Calculer_limites(Rayon_horizontal+1,Rayon_vertical+1); + + + + // Affichage des extremitées de l'ellipse sur chaque quart de l'ellipse: + + for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=-Rayon_vertical;Pos_YLimite_Bas) + + Fin_Y=Limite_Bas; + + if (Debut_XLimite_Droite) + + Fin_X=Limite_Droite; + + + + // Affichage de l'ellipse + + for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Ellipse_Curseur_Y++) + + for (Pos_X=Debut_X,Ellipse_Curseur_X=Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Ellipse_Curseur_X++) + + if (Pixel_dans_ellipse()) + + Afficher_pixel(Pos_X,Pos_Y,Couleur); + + Mettre_Ecran_A_Jour(Centre_X-Rayon_horizontal,Centre_Y-Rayon_vertical,2*Rayon_horizontal+1,2*Rayon_vertical+1); + +} + + + +/****************** + +* TRACÉ DE LIGNES * + +******************/ + + + +void Rectifier_coordonnees_a_45_degres(short AX, short AY, short* BX, short* BY) + +// Modifie BX et BY pour que la ligne AXAY - BXBY soit + +// - une droite horizontale + +// - une droite verticale + +// - une droite avec une pente de 45 degrés + +{ + + int dx, dy; + + float tan; + + + + dx = (*BX)-AX; + + dy = AY- *BY; // On prend l'opposée car à l'écran les Y sont positifs en bas, et en maths, positifs en haut + + + + if (dx==0) return; // On est en lockx et de toutes façons le X n'a pas bougé, on sort tout de suite pour éviter une méchante division par 0 + + + + tan = (float)dy/(float)dx; + + + + if (tan <= 0.4142 && tan >= -0.4142) + + { + + // Cas 1 : Lock Y + + *BY = AY; + + } + + else if ( tan > 0.4142 && tan < 2.4142) + + { + + // Cas 2 : dy=dx + + int nBY = AY - dx; + + *BY = (*BY + nBY)/2; + + *BX = AX + AY - *BY; + + } + + else if (tan < -0.4142 && tan >= -2.4142) + + { + + // Cas 8 : dy = -dx + + int nBY = AY + dx; + + *BY = (*BY + nBY)/2; + + *BX = AX - AY + *BY; + + } + + else + + { + + // Cas 3 : Lock X + + *BX = AX; + + } + + + + return; + +} + + + + // -- Tracer général d'une ligne ------------------------------------------ + + + +void Tracer_ligne_General(short Debut_X,short Debut_Y,short Fin_X,short Fin_Y, byte Couleur) + +{ + + short Pos_X,Pos_Y; + + short Incr_X,Incr_Y; + + short i,Cumul; + + short Delta_X,Delta_Y; + + + + + + Pos_X=Debut_X; + + Pos_Y=Debut_Y; + + + + if (Debut_XDelta_X) + + { + + Cumul=Delta_Y>>1; + + for (i=1; i=Delta_Y) + + { + + Cumul-=Delta_Y; + + Pos_X+=Incr_X; + + } + + Pixel_figure(Pos_X,Pos_Y,Couleur); + + } + + } + + else + + { + + Cumul=Delta_X>>1; + + for (i=1; i=Delta_X) + + { + + Cumul-=Delta_X; + + Pos_Y+=Incr_Y; + + } + + Pixel_figure(Pos_X,Pos_Y,Couleur); + + } + + } + + + + if ( (Debut_X!=Fin_X) || (Debut_Y!=Fin_Y) ) + + Pixel_figure(Fin_X,Fin_Y,Couleur); + + + +} + + + + // -- Tracer définitif d'une ligne -- + + + +void Tracer_ligne_Definitif(short Debut_X,short Debut_Y,short Fin_X,short Fin_Y, byte Couleur) + +{ + + + + int L = Fin_X-Debut_X, H = Fin_Y - Debut_Y; + + Pixel_figure=Pixel_figure_Definitif; + + Tracer_ligne_General(Debut_X,Debut_Y,Fin_X,Fin_Y,Couleur); + + Mettre_Ecran_A_Jour((Debut_XFin_X) + + { + + Tempo=Debut_X; + + Debut_X=Fin_X; + + Fin_X=Tempo; + + } + + if (Debut_Y>Fin_Y) + + { + + Tempo=Debut_Y; + + Debut_Y=Fin_Y; + + Fin_Y=Tempo; + + } + + + + // On trace le rectangle: + + + + for (Pos_X=Debut_X;Pos_X<=Fin_X;Pos_X++) + + Afficher_pinceau(Pos_X,Debut_Y,Couleur,0); + + + + for (Pos_Y=Debut_Y+1;Pos_YFin_X) + + { + + Tempo=Debut_X; + + Debut_X=Fin_X; + + Fin_X=Tempo; + + } + + if (Debut_Y>Fin_Y) + + { + + Tempo=Debut_Y; + + Debut_Y=Fin_Y; + + Fin_Y=Tempo; + + } + + + + // Correction en cas de dépassement des limites de l'image + + if (Fin_X>Limite_Droite) + + Fin_X=Limite_Droite; + + if (Fin_Y>Limite_Bas) + + Fin_Y=Limite_Bas; + + + + // On trace le rectangle: + + for (Pos_Y=Debut_Y;Pos_Y<=Fin_Y;Pos_Y++) + + for (Pos_X=Debut_X;Pos_X<=Fin_X;Pos_X++) + + // Afficher_pixel traite chaque pixel avec tous les effets ! (smear, ...) + + // Donc on ne peut pas otimiser en traçant ligne par ligne avec memset :( + + Afficher_pixel(Pos_X,Pos_Y,Couleur); + + Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X-Debut_X,Fin_Y-Debut_Y); + + + +} + + + + + + + + + + // -- Tracer une courbe de Bézier -- + + + +void Tracer_courbe_General(short X1, short Y1, + + short X2, short Y2, + + short X3, short Y3, + + short X4, short Y4, + + byte Couleur) + +{ + + float Delta,T,T2,T3; + + short X,Y,Old_X,Old_Y; + + word i; + + int CX[4]; + + int CY[4]; + + + + // Calcul des vecteurs de coefficients + + CX[0]= - X1 + 3*X2 - 3*X3 + X4; + + CX[1]= + 3*X1 - 6*X2 + 3*X3; + + CX[2]= - 3*X1 + 3*X2; + + CX[3]= + X1; + + CY[0]= - Y1 + 3*Y2 - 3*Y3 + Y4; + + CY[1]= + 3*Y1 - 6*Y2 + 3*Y3; + + CY[2]= - 3*Y1 + 3*Y2; + + CY[3]= + Y1; + + + + // Traçage de la courbe + + Old_X=X1; + + Old_Y=Y1; + + Pixel_figure(Old_X,Old_Y,Couleur); + + Delta=0.05; // 1.0/20 + + T=0; + + for (i=1; i<=20; i++) + + { + + T=T+Delta; T2=T*T; T3=T2*T; + + X=Round(T3*CX[0] + T2*CX[1] + T*CX[2] + CX[3]); + + Y=Round(T3*CY[0] + T2*CY[1] + T*CY[2] + CY[3]); + + Tracer_ligne_General(Old_X,Old_Y,X,Y,Couleur); + + Old_X=X; + + Old_Y=Y; + + } + + + + X = Min(Min(X1,X2),Min(X3,X4)); + + Y = Min(Min(Y1,Y2),Min(Y3,Y4)); + + Old_X = Max(Max(X1,X2),Max(X3,X4)) - X; + + Old_Y = Max(Max(Y1,Y2),Max(Y3,Y4)) - Y; + + Mettre_Ecran_A_Jour(X,Y,Old_X+1,Old_Y+1); + +} + + + + // -- Tracer une courbe de Bézier définitivement -- + + + +void Tracer_courbe_Definitif(short X1, short Y1, + + short X2, short Y2, + + short X3, short Y3, + + short X4, short Y4, + + byte Couleur) + +{ + + Pixel_figure=Pixel_figure_Definitif; + + Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); + +} + + + + // -- Tracer la preview d'une courbe de Bézier -- + + + +void Tracer_courbe_Preview(short X1, short Y1, + + short X2, short Y2, + + short X3, short Y3, + + short X4, short Y4, + + byte Couleur) + +{ + + Pixel_figure=Pixel_figure_Preview; + + Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); + +} + + + + // -- Effacer la preview d'une courbe de Bézier -- + + + +void Effacer_courbe_Preview(short X1, short Y1, + + short X2, short Y2, + + short X3, short Y3, + + short X4, short Y4, + + byte Couleur) + +{ + + Pixel_figure=Pixel_figure_Effacer_preview; + + Tracer_courbe_General(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Couleur); + +} + + + + + + + + + + // -- Spray : un petit coup de Pschiitt! -- + + + +void Aerographe(short Bouton_clicke) + +{ + + short Pos_X,Pos_Y; + + short Rayon=Spray_Size>>1; + + long Rayon_au_carre=(long)Rayon*Rayon; + + short Indice,Count; + + byte Indice_couleur; + + byte Sens; + + + + + + Effacer_curseur(); + + + + if (Spray_Mode) + + { + + for (Count=1; Count<=Spray_Mono_flow; Count++) + + { + + Pos_X=(rand()%Spray_Size)-Rayon; + + Pos_Y=(rand()%Spray_Size)-Rayon; + + if ( (Pos_X*Pos_X)+(Pos_Y*Pos_Y) <= Rayon_au_carre ) + + { + + Pos_X+=Pinceau_X; + + Pos_Y+=Pinceau_Y; + + if (Bouton_clicke==1) + + Afficher_pinceau(Pos_X,Pos_Y,Fore_color,0); + + else + + Afficher_pinceau(Pos_X,Pos_Y,Back_color,0); + + } + + } + + } + + else + + { + + // On essaye de se balader dans la table des flux de façon à ce que ce + + // ne soit pas toujours la dernière couleur qui soit affichée en dernier + + // Pour ça, on part d'une couleur au pif dans une direction aléatoire. + + Sens=rand()&1; + + for (Indice=0,Indice_couleur=rand()/*%256*/; Indice<256; Indice++) + + { + + for (Count=1; Count<=Spray_Multi_flow[Indice_couleur]; Count++) + + { + + Pos_X=(rand()%Spray_Size)-Rayon; + + Pos_Y=(rand()%Spray_Size)-Rayon; + + if ( (Pos_X*Pos_X)+(Pos_Y*Pos_Y) <= Rayon_au_carre ) + + { + + Pos_X+=Pinceau_X; + + Pos_Y+=Pinceau_Y; + + if (Bouton_clicke==A_GAUCHE) + + Afficher_pinceau(Pos_X,Pos_Y,Indice_couleur,0); + + else + + Afficher_pinceau(Pos_X,Pos_Y,Back_color,0); + + } + + } + + if (Sens) + + Indice_couleur++; + + else + + Indice_couleur--; + + } + + } + + + + Afficher_curseur(); + + + + for (Count=1; Count<=Spray_Delay; Count++) + + Wait_VBL(); + +} + + + + + + + + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////// GESTION DES DEGRADES ////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + + + + + + // -- Gestion d'un dégradé de base (le plus moche) -- + + + +void Degrade_de_base(long Indice,short Pos_X,short Pos_Y) + +{ + + long Position; + + + + // On fait un premier calcul partiel + + Position=(Indice*Degrade_Intervalle_bornes); + + + + // On gère un déplacement au hasard + + Position+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; + + Position-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; + + + + Position/=Degrade_Intervalle_total; + + + + // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors + + // des valeurs autorisées par le dégradé défini par l'utilisateur. + + + + if (Position<0) + + Position=0; + + else if (Position>=Degrade_Intervalle_bornes) + + Position=Degrade_Intervalle_bornes-1; + + + + // On ramène ensuite la position dans le dégradé vers un numéro de couleur + + if (Degrade_Inverse) + + Traiter_pixel_de_degrade(Pos_X,Pos_Y,Degrade_Borne_Superieure-Position); + + else + + Traiter_pixel_de_degrade(Pos_X,Pos_Y,Degrade_Borne_Inferieure+Position); + +} + + + + + + // -- Gestion d'un dégradé par trames simples -- + + + +void Degrade_de_trames_simples(long Indice,short Pos_X,short Pos_Y) + +{ + + long Position_dans_degrade; + + long Position_dans_segment; + + + + // + + // But de l'opération: en plus de calculer la position de base (désignée + + // dans cette procédure par "Position_dans_degrade", on calcule la position + + // de l'indice dans le schéma suivant: + + // + + // | Les indices qui traînent de ce côté du segment se voient subir + + // | une incrémentation conditionnelle à leur position dans l'écran. + + // v + + // |---|---|---|---- - - - + + // ^ + + // |_ Les indices qui traînent de ce côté du segment se voient subir une + + // décrémentation conditionnelle à leur position dans l'écran. + + + + // On fait d'abord un premier calcul partiel + + Position_dans_degrade=(Indice*Degrade_Intervalle_bornes); + + + + // On gère un déplacement au hasard... + + Position_dans_degrade+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; + + Position_dans_degrade-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; + + + + if (Position_dans_degrade<0) + + Position_dans_degrade=0; + + + + // ... qui nous permet de calculer la position dans le segment + + Position_dans_segment=((Position_dans_degrade<<2)/Degrade_Intervalle_total)&3; + + + + // On peut ensuite terminer le calcul de l'indice dans le dégradé + + Position_dans_degrade/=Degrade_Intervalle_total; + + + + // On va pouvoir discuter de la valeur de Position_dans_degrade en fonction + + // de la position dans l'écran et de la Position_dans_segment. + + + + switch (Position_dans_segment) + + { + + case 0 : // On est sur la gauche du segment + + if (((Pos_X+Pos_Y)&1)==0) + + Position_dans_degrade--; + + break; + + + + // On n'a pas à traiter les cas 1 et 2 car ils représentent des valeurs + + // suffisament au centre du segment pour ne pas avoir à subir la trame + + + + case 3 : // On est sur la droite du segment + + if (((Pos_X+Pos_Y)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame. + + Position_dans_degrade++; + + } + + + + // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors + + // des valeurs autorisées par le dégradé défini par l'utilisateur. + + + + if (Position_dans_degrade<0) + + Position_dans_degrade=0; + + else if (Position_dans_degrade>=Degrade_Intervalle_bornes) + + Position_dans_degrade=Degrade_Intervalle_bornes-1; + + + + // On ramène ensuite la position dans le dégradé vers un numéro de couleur + + if (Degrade_Inverse) + + Position_dans_degrade=Degrade_Borne_Superieure-Position_dans_degrade; + + else + + Position_dans_degrade=Degrade_Borne_Inferieure+Position_dans_degrade; + + + + Traiter_pixel_de_degrade(Pos_X,Pos_Y,Position_dans_degrade); + +} + + + + + + // -- Gestion d'un dégradé par trames étendues -- + + + +void Degrade_de_trames_etendues(long Indice,short Pos_X,short Pos_Y) + +{ + + long Position_dans_degrade; + + long Position_dans_segment; + + + +// + + // But de l'opération: en plus de calculer la position de base (désignée + + // dans cette procédure par "Position_dans_degrade", on calcule la position + + // de l'indice dans le schéma suivant: + + // + + // | Les indices qui traînent de ce côté du segment se voient subir + + // | une incrémentation conditionnelle à leur position dans l'écran. + + // v + + // |---|---|---|---- - - - + + // ^ + + // |_ Les indices qui traînent de ce côté du segment se voient subir une + + // décrémentation conditionnelle à leur position dans l'écran. + + + + // On fait d'abord un premier calcul partiel + + Position_dans_degrade=(Indice*Degrade_Intervalle_bornes); + + + + // On gère un déplacement au hasard + + Position_dans_degrade+=(Degrade_Intervalle_total*(rand()%Degrade_Melange_aleatoire)) >>6; + + Position_dans_degrade-=(Degrade_Intervalle_total*Degrade_Melange_aleatoire) >>7; + + + + if (Position_dans_degrade<0) + + Position_dans_degrade=0; + + + + // Qui nous permet de calculer la position dans le segment + + Position_dans_segment=((Position_dans_degrade<<3)/Degrade_Intervalle_total)&7; + + + + // On peut ensuite terminer le calcul de l'indice dans le dégradé + + Position_dans_degrade/=Degrade_Intervalle_total; + + + + // On va pouvoir discuter de la valeur de Position_dans_degrade en fonction + + // de la position dans l'écran et de la Position_dans_segment. + + + + switch (Position_dans_segment) + + { + + case 0 : // On est sur l'extrême gauche du segment + + if (((Pos_X+Pos_Y)&1)==0) + + Position_dans_degrade--; + + break; + + + + case 1 : // On est sur la gauche du segment + + case 2 : // On est sur la gauche du segment + + if (((Pos_X & 1)==0) && ((Pos_Y & 1)==0)) + + Position_dans_degrade--; + + break; + + + + // On n'a pas à traiter les cas 3 et 4 car ils représentent des valeurs + + // suffisament au centre du segment pour ne pas avoir à subir la trame + + + + case 5 : // On est sur la droite du segment + + case 6 : // On est sur la droite du segment + + if (((Pos_X & 1)==0) && ((Pos_Y & 1)!=0)) + + Position_dans_degrade++; + + break; + + + + case 7 : // On est sur l'extreme droite du segment + + if (((Pos_X+Pos_Y)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame. + + Position_dans_degrade++; + + } + + + + // On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors + + // des valeurs autorisées par le dégradé défini par l'utilisateur. + + + + if (Position_dans_degrade<0) + + Position_dans_degrade=0; + + else if (Position_dans_degrade>=Degrade_Intervalle_bornes) + + Position_dans_degrade=Degrade_Intervalle_bornes-1; + + + + // On ramène ensuite la position dans le dégradé vers un numéro de couleur + + if (Degrade_Inverse) + + Position_dans_degrade=Degrade_Borne_Superieure-Position_dans_degrade; + + else + + Position_dans_degrade=Degrade_Borne_Inferieure+Position_dans_degrade; + + + + Traiter_pixel_de_degrade(Pos_X,Pos_Y,Position_dans_degrade); + +} + + + + + + + + // -- Tracer un cercle degradé (une sphère) -- + + + +void Tracer_cercle_degrade(short Centre_X,short Centre_Y,short Rayon,short Eclairage_X,short Eclairage_Y) + +{ + + long Debut_X; + + long Debut_Y; + + long Pos_X; + + long Pos_Y; + + long Fin_X; + + long Fin_Y; + + long Distance_X; // Distance (au carré) sur les X du point en cours au centre d'éclairage + + long Distance_Y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage + + + + Debut_X=Centre_X-Rayon; + + Debut_Y=Centre_Y-Rayon; + + Fin_X=Centre_X+Rayon; + + Fin_Y=Centre_Y+Rayon; + + + + // Correction des bornes d'après les limites + + if (Debut_YLimite_Bas) + + Fin_Y=Limite_Bas; + + if (Debut_XLimite_Droite) + + Fin_X=Limite_Droite; + + + + Degrade_Intervalle_total=Cercle_Limite+ + + ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ + + ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y))+ + + (2L*Rayon*sqrt( + + ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ + + ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y)))); + + + + if (Degrade_Intervalle_total==0) + + Degrade_Intervalle_total=1; + + + + // Affichage du cercle + + for (Pos_Y=Debut_Y,Cercle_Curseur_Y=(long)Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Cercle_Curseur_Y++) + + { + + Distance_Y =(Pos_Y-Eclairage_Y); + + Distance_Y*=Distance_Y; + + for (Pos_X=Debut_X,Cercle_Curseur_X=(long)Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Cercle_Curseur_X++) + + if (Pixel_dans_cercle()) + + { + + Distance_X =(Pos_X-Eclairage_X); + + Distance_X*=Distance_X; + + Traiter_degrade(Distance_X+Distance_Y,Pos_X,Pos_Y); + + } + + } + + + + Mettre_Ecran_A_Jour(Centre_X-Rayon,Centre_Y-Rayon,2*Rayon+1,2*Rayon+1); + +} + + + + + + // -- Tracer une ellipse degradée -- + + + +void Tracer_ellipse_degradee(short Centre_X,short Centre_Y,short Rayon_horizontal,short Rayon_vertical,short Eclairage_X,short Eclairage_Y) + +{ + + long Debut_X; + + long Debut_Y; + + long Pos_X; + + long Pos_Y; + + long Fin_X; + + long Fin_Y; + + long Distance_X; // Distance (au carré) sur les X du point en cours au centre d'éclairage + + long Distance_Y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage + + + + + + Debut_X=Centre_X-Rayon_horizontal; + + Debut_Y=Centre_Y-Rayon_vertical; + + Fin_X=Centre_X+Rayon_horizontal; + + Fin_Y=Centre_Y+Rayon_vertical; + + + + // Calcul des limites de l'ellipse + + Ellipse_Calculer_limites(Rayon_horizontal+1,Rayon_vertical+1); + + + + // On calcule la distance maximale: + + Degrade_Intervalle_total=(Rayon_horizontal*Rayon_horizontal)+ + + (Rayon_vertical*Rayon_vertical)+ + + ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ + + ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y))+ + + (2L + + *sqrt( + + (Rayon_horizontal*Rayon_horizontal)+ + + (Rayon_vertical *Rayon_vertical )) + + *sqrt( + + ((Centre_X-Eclairage_X)*(Centre_X-Eclairage_X))+ + + ((Centre_Y-Eclairage_Y)*(Centre_Y-Eclairage_Y)))); + + + + if (Degrade_Intervalle_total==0) + + Degrade_Intervalle_total=1; + + + + // Correction des bornes d'après les limites + + if (Debut_YLimite_Bas) + + Fin_Y=Limite_Bas; + + if (Debut_XLimite_Droite) + + Fin_X=Limite_Droite; + + + + // Affichage de l'ellipse + + for (Pos_Y=Debut_Y,Ellipse_Curseur_Y=Debut_Y-Centre_Y;Pos_Y<=Fin_Y;Pos_Y++,Ellipse_Curseur_Y++) + + { + + Distance_Y =(Pos_Y-Eclairage_Y); + + Distance_Y*=Distance_Y; + + for (Pos_X=Debut_X,Ellipse_Curseur_X=Debut_X-Centre_X;Pos_X<=Fin_X;Pos_X++,Ellipse_Curseur_X++) + + if (Pixel_dans_ellipse()) + + { + + Distance_X =(Pos_X-Eclairage_X); + + Distance_X*=Distance_X; + + Traiter_degrade(Distance_X+Distance_Y,Pos_X,Pos_Y); + + } + + } + + + + Mettre_Ecran_A_Jour(Debut_X,Debut_Y,Fin_X-Debut_X+1,Fin_Y-Debut_Y+1); + +} + + + + + +// Tracé d'un rectangle (RAX RAY - RBX RBY) dégradé selon le vecteur (VAX VAY - VBX - VBY) + +void Tracer_rectangle_degrade(short RAX,short RAY,short RBX,short RBY,short VAX,short VAY, short VBX, short VBY) + +{ + + short Pos_Y, Pos_X; + + + + // On commence par s'assurer que le rectangle est à l'endroit + + if(RBX < RAX) + + { + + Pos_X = RBX; + + RBX = RAX; + + RAX = Pos_X; + + } + + + + if(RBY < RAY) + + { + + Pos_Y = RBY; + + RBY = RAY; + + RAY = Pos_Y; + + } + + + + // Correction des bornes d'après les limites + + if (RAYLimite_Bas) + + RBY=Limite_Bas; + + if (RAXLimite_Droite) + + RBX=Limite_Droite; + + + + if(VBX == VAX) + + { + + // Le vecteur est vertical, donc on évite la partie en dessous qui foirerait avec une division par 0... + + if (VBY == VAY) return; // L'utilisateur fait n'importe quoi + + Degrade_Intervalle_total = abs(VBY - VAY); + + for(Pos_Y=RAY;Pos_Y<=RBY;Pos_Y++) + + for(Pos_X=RAX;Pos_X<=RBX;Pos_X++) + + Traiter_degrade(abs(VBY - Pos_Y),Pos_X,Pos_Y); + + + + } + + else + + { + + float a; + + float b; + + float Distance_X, Distance_Y; + + + + Degrade_Intervalle_total = sqrt(pow(VBY - VAY,2)+pow(VBX - VAX,2)); + + a = (float)(VBY - VAY)/(float)(VBX - VAX); + + b = VAY - a*VAX; + + + + for (Pos_Y=RAY;Pos_Y<=RBY;Pos_Y++) + + for (Pos_X = RAX;Pos_X<=RBX;Pos_X++) + + { + + // On calcule ou on en est dans le dégradé + + Distance_X = pow((Pos_Y - VAY),2)+pow((Pos_X - VAX),2); + + Distance_Y = pow((-a * Pos_X + Pos_Y - b),2)/(a*a+1); + + + + Traiter_degrade((int)sqrt(Distance_X - Distance_Y),Pos_X,Pos_Y); + + } + + } + + Mettre_Ecran_A_Jour(RAX,RAY,RBX,RBY); + +} + + + + + + + + + +// -- Tracer un polygône plein -- + + + +typedef struct POLYGON_EDGE /* an active edge */ + +{ + + short top; /* top y position */ + + short bottom; /* bottom y position */ + + float x, dx; /* floating point x position and gradient */ + + float w; /* width of line segment */ + + struct POLYGON_EDGE *prev; /* doubly linked list */ + + struct POLYGON_EDGE *next; + +} POLYGON_EDGE; + + + + + + + +/* fill_edge_structure: + + * Polygon helper function: initialises an edge structure for the 2d + + * rasteriser. + + */ + +void fill_edge_structure(POLYGON_EDGE *edge, short *i1, short *i2) + +{ + + short *it; + + + + if (i2[1] < i1[1]) + + { + + it = i1; + + i1 = i2; + + i2 = it; + + } + + + + edge->top = i1[1]; + + edge->bottom = i2[1] - 1; + + edge->dx = ((float) i2[0] - (float) i1[0]) / ((float) i2[1] - (float) i1[1]); + + edge->x = i1[0] + 0.4999999; + + edge->prev = NULL; + + edge->next = NULL; + + + + if (edge->dx+1 < 0.0) + + edge->x += edge->dx+1; + + + + if (edge->dx >= 0.0) + + edge->w = edge->dx; + + else + + edge->w = -(edge->dx); + + + + if (edge->w-1.0<0.0) + + edge->w = 0.0; + + else + + edge->w = edge->w-1; + +} + + + + + + + +/* add_edge: + + * Adds an edge structure to a linked list, returning the new head pointer. + + */ + +POLYGON_EDGE * add_edge(POLYGON_EDGE *list, POLYGON_EDGE *edge, int sort_by_x) + +{ + + POLYGON_EDGE *pos = list; + + POLYGON_EDGE *prev = NULL; + + + + if (sort_by_x) + + { + + while ( (pos) && ((pos->x+((pos->w+pos->dx)/2)) < (edge->x+((edge->w+edge->dx)/2))) ) + + { + + prev = pos; + + pos = pos->next; + + } + + } + + else + + { + + while ((pos) && (pos->top < edge->top)) + + { + + prev = pos; + + pos = pos->next; + + } + + } + + + + edge->next = pos; + + edge->prev = prev; + + + + if (pos) + + pos->prev = edge; + + + + if (prev) + + { + + prev->next = edge; + + return list; + + } + + else + + return edge; + +} + + + + + + + +/* remove_edge: + + * Removes an edge structure from a list, returning the new head pointer. + + */ + +POLYGON_EDGE * remove_edge(POLYGON_EDGE *list, POLYGON_EDGE *edge) + +{ + + if (edge->next) + + edge->next->prev = edge->prev; + + + + if (edge->prev) + + { + + edge->prev->next = edge->next; + + return list; + + } + + else + + return edge->next; + +} + + + + + + + +/* polygon: + + * Draws a filled polygon with an arbitrary number of corners. Pass the + + * number of vertices, then an array containing a series of x, y points + + * (a total of vertices*2 values). + + */ + +void Polyfill_General(int Vertices, short * Points, int Color) + +{ + + short c; + + short top = 0x7FFF; + + short bottom = 0; + + short *i1, *i2; + + short Pos_X,Fin_X; + + POLYGON_EDGE *edge, *next_edge, *initial_edge; + + POLYGON_EDGE *active_edges = NULL; + + POLYGON_EDGE *inactive_edges = NULL; + + + + + + /* allocate some space and fill the edge table */ + + initial_edge=edge=(POLYGON_EDGE *) malloc(sizeof(POLYGON_EDGE) * Vertices); + + + + i1 = Points; + + i2 = Points + ((Vertices-1)<<1); + + + + for (c=0; cbottom >= edge->top) + + { + + if (edge->top < top) + + top = edge->top; + + + + if (edge->bottom > bottom) + + bottom = edge->bottom; + + + + inactive_edges = add_edge(inactive_edges, edge, 0); + + edge++; + + } + + } + + i2 = i1; + + i1 += 2; + + } + + + + /* for each scanline in the polygon... */ + + for (c=top; c<=bottom; c++) + + { + + /* check for newly active edges */ + + edge = inactive_edges; + + while ((edge) && (edge->top == c)) + + { + + next_edge = edge->next; + + inactive_edges = remove_edge(inactive_edges, edge); + + active_edges = add_edge(active_edges, edge, 1); + + edge = next_edge; + + } + + + + /* draw horizontal line segments */ + + if ((c>=Limite_Haut) && (c<=Limite_Bas)) + + { + + edge = active_edges; + + while ((edge) && (edge->next)) + + { + + Pos_X=/*Round*/(edge->x); + + Fin_X=/*Round*/(edge->next->x+edge->next->w); + + if (Pos_XLimite_Droite) + + Fin_X=Limite_Droite; + + for (; Pos_X<=Fin_X; Pos_X++) + + Pixel_figure(Pos_X,c,Color); + + edge = edge->next->next; + + } + + } + + + + /* update edges, sorting and removing dead ones */ + + edge = active_edges; + + while (edge) + + { + + next_edge = edge->next; + + if (c >= edge->bottom) + + active_edges = remove_edge(active_edges, edge); + + else + + { + + edge->x += edge->dx; + + while ((edge->prev) && ( (edge->x+(edge->w/2)) < (edge->prev->x+(edge->prev->w/2))) ) + + { + + if (edge->next) + + edge->next->prev = edge->prev; + + edge->prev->next = edge->next; + + edge->next = edge->prev; + + edge->prev = edge->prev->prev; + + edge->next->prev = edge; + + if (edge->prev) + + edge->prev->next = edge; + + else + + active_edges = edge; + + } + + } + + edge = next_edge; + + } + + } + + + + free(initial_edge); + + // On ne connait pas simplement les xmin et xmax ici, mais de toutes façon ce n'est pas utilisé en preview + + Mettre_Ecran_A_Jour(0,top,Principal_Largeur_image,bottom-top+1); + +} + + + + + +void Polyfill(int Vertices, short * Points, int Color) + +{ + + Pixel_figure=Afficher_pixel; + + Polyfill_General(Vertices,Points,Color); + +} + + + + + + + +//------------ Remplacement de la couleur pointée par une autre -------------- + + + +void Remplacer(byte Nouvelle_couleur) + +{ + + byte Ancienne_couleur; + + + + if ((Pinceau_X255)) + + Indice++; + + + + // On note la position de la première case de la séquence + + Premier=Indice; + + + + // On recherche la position de la dernière case de la séquence + + for (Dernier=Premier;Liste[Dernier+1]<256;Dernier++); + + + + // Pour toutes les cases non vides (et non inhibées) qui suivent + + switch (Mode) + + { + + case MODE_SHADE_NORMAL : + + for (;(Indice<512) && (Liste[Indice]<256);Indice++) + + { // On met à jour les tables de conversion + + Couleur=Liste[Indice]; + + Table_inc[Couleur]=Liste[(Indice+Pas<=Dernier)?Indice+Pas:Dernier]; + + Table_dec[Couleur]=Liste[(Indice-Pas>=Premier)?Indice-Pas:Premier]; + + } + + break; + + case MODE_SHADE_BOUCLE : + + Temp=1+Dernier-Premier; + + for (;(Indice<512) && (Liste[Indice]<256);Indice++) + + { // On met à jour les tables de conversion + + Couleur=Liste[Indice]; + + Table_inc[Couleur]=Liste[Premier+((Pas+Indice-Premier)%Temp)]; + + Table_dec[Couleur]=Liste[Premier+(((Temp-Pas)+Indice-Premier)%Temp)]; + + } + + break; + + default : // MODE_SHADE_NOSAT + + for (;(Indice<512) && (Liste[Indice]<256);Indice++) + + { // On met à jour les tables de conversion + + Couleur=Liste[Indice]; + + if (Indice+Pas<=Dernier) + + Table_inc[Couleur]=Liste[Indice+Pas]; + + if (Indice-Pas>=Premier) + + Table_dec[Couleur]=Liste[Indice-Pas]; + + } + + } + + } + +} + + + + + + + +// -- Interface avec l'image, affectée par le facteur de grossissement ------- + + + + // fonction d'affichage "Pixel" utilisée pour les opérations définitivement + + // Ne doit à aucune condition être appelée en dehors de la partie visible + + // de l'image dans l'écran (ça pourrait être grave) + +void Afficher_pixel(word X,word Y,byte Couleur) + + // X & Y sont la position d'un point dans l'IMAGE + + // Couleur est la couleur du point + + // Le Stencil est géré. + + // Les effets sont gérés par appel à Fonction_effet(). + + // La Loupe est gérée par appel à Pixel_Preview(). + +{ + + if ( ( (!Trame_Mode) || (Effet_Trame(X,Y)) ) + + && (!((Stencil_Mode) && (Stencil[Lit_pixel_dans_ecran_courant(X,Y)]))) + + && (!((Mask_Mode) && (Mask_table[Lit_pixel_dans_ecran_brouillon(X,Y)]))) ) + + { + + Couleur=Fonction_effet(X,Y,Couleur); + + Pixel_dans_ecran_courant(X,Y,Couleur); + + Pixel_Preview(X,Y,Couleur); + + } + +} + + + + + + + +// -- Calcul des différents effets ------------------------------------------- + + + + // -- Aucun effet en cours -- + + + +byte Aucun_effet(__attribute__((unused)) word X,__attribute__((unused)) word Y,byte Couleur) + +{ + + return Couleur; + +} + + + + // -- Effet de Shading -- + + + +byte Effet_Shade(word X,word Y,__attribute__((unused)) byte Couleur) + +{ + + return Shade_Table[Lit_pixel_dans_ecran_feedback(X,Y)]; + +} + + + +byte Effet_Quick_shade(word X,word Y,byte Couleur) + +{ + + int C=Couleur=Lit_pixel_dans_ecran_feedback(X,Y); + + int Sens=(Fore_color<=Back_color); + + byte Debut,Fin; + + int Largeur; + + + + if (Sens) + + { + + Debut=Fore_color; + + Fin =Back_color; + + } + + else + + { + + Debut=Back_color; + + Fin =Fore_color; + + } + + + + if ((C>=Debut) && (C<=Fin) && (Debut!=Fin)) + + { + + Largeur=1+Fin-Debut; + + + + if ( ((Mouse_K==A_GAUCHE) && Sens) || ((Mouse_K==A_DROITE) && (!Sens)) ) + + C-=Quick_shade_Step%Largeur; + + else + + C+=Quick_shade_Step%Largeur; + + + + if (CFin) + + switch (Quick_shade_Loop) + + { + + case MODE_SHADE_NORMAL : return Fin; + + case MODE_SHADE_BOUCLE : return (C-Largeur); + + default : return Couleur; + + } + + } + + + + return C; + +} + + + + // -- Effet de Tiling -- + + + +byte Effet_Tiling(word X,word Y,__attribute__((unused)) byte Couleur) + +{ + + return Lit_pixel_dans_brosse((X+Brosse_Largeur-Tiling_Decalage_X)%Brosse_Largeur, + + (Y+Brosse_Hauteur-Tiling_Decalage_Y)%Brosse_Hauteur); + +} + + + + // -- Effet de Smooth -- + + + +byte Effet_Smooth(word X,word Y,__attribute__((unused)) byte Couleur) + +{ + + int R,V,B; + + byte C; + + int Poids,Poids_total; + + byte X2=((X+1)