/* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2008 Yves Rizoud 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. */ #include #include #include #include "struct.h" #include "sdlscreen.h" #include "global.h" #include "graph.h" //Afficher_curseur #include "erreurs.h" #include "boutons.h" #include "moteur.h" #include "divers.h" #include "clavier.h" #include "sdlscreen.h" #include "windows.h" word Palette_Compter_nb_couleurs_utilisees(dword* Tableau) { int Nombre_De_Pixels=0; Uint8* Pixel_Courant=Principal_Ecran; Uint8 Couleur; word Nombre_Couleurs=0; int i; for (i=0;i<256;i++) Tableau[i]=0; //Calcul du nombre de pixels dans l'image Nombre_De_Pixels=Principal_Hauteur_image*Principal_Largeur_image; // On parcourt l'écran courant pour compter les utilisations des couleurs for(i=0;i Principal_Split) INPUT_Nouveau_Mouse_Y=INPUT_Nouveau_Mouse_Y Principal_Split) { INPUT_Nouveau_Mouse_Y+=Loupe_Facteur; if (INPUT_Nouveau_Mouse_Y>=Hauteur_ecran) INPUT_Nouveau_Mouse_Y=Hauteur_ecran-1; } else INPUT_Nouveau_Mouse_Y++; ok=1; } } else if(Touche == Config_Touche[2]) { //[Touche] = Emulation de MOUSE LEFT if(INPUT_Nouveau_Mouse_X!=0) { if(Loupe_Mode && INPUT_Nouveau_Mouse_Y < Menu_Ordonnee && INPUT_Nouveau_Mouse_X > Principal_Split) INPUT_Nouveau_Mouse_X-=Loupe_Facteur; else INPUT_Nouveau_Mouse_X--; ok=1; } } else if(Touche == Config_Touche[3]) { //[Touche] = Emulation de MOUSE RIGHT if(INPUT_Nouveau_Mouse_X Principal_Split) { INPUT_Nouveau_Mouse_X+=Loupe_Facteur; if (INPUT_Nouveau_Mouse_X>=Largeur_ecran) INPUT_Nouveau_Mouse_X=Largeur_ecran-1; } else INPUT_Nouveau_Mouse_X++; ok=1; } } else if(Touche == Config_Touche[4]) { //[Touche] = Emulation de MOUSE CLICK LEFT INPUT_Nouveau_Mouse_K=1; ok=1; } else if(Touche == Config_Touche[5]) { //[Touche] = Emulation de MOUSE CLICK RIGHT INPUT_Nouveau_Mouse_K=2; ok=1; } if(ok) { SDL_WarpMouse( INPUT_Nouveau_Mouse_X*Pixel_width, INPUT_Nouveau_Mouse_Y*Pixel_height ); } } break; } } else { Flush_update(); return 0; // Il ne s'est rien passé } //Gestion "avancée" du curseur: interdire la descente du curseur dans le //menu lorsqu'on est en train de travailler dans l'image if(Operation_Taille_pile!=0) { byte bl=0;//BL va indiquer si on doit corriger la position du curseur //Si le curseur ne se trouve plus dans l'image if(Menu_Ordonnee<=INPUT_Nouveau_Mouse_Y) { //On bloque le curseur en fin d'image bl++; INPUT_Nouveau_Mouse_Y=Menu_Ordonnee-1; //La ligne !!au-dessus!! du menu } if(Loupe_Mode) { if(Operation_dans_loupe==0) { if(INPUT_Nouveau_Mouse_X>=Principal_Split) { bl++; INPUT_Nouveau_Mouse_X=Principal_Split-1; } } else { if(INPUT_Nouveau_Mouse_X0;dx--) { esi = Debut_de_colonne; // Pout chaque colonne for(cx=Brosse_Hauteur;cx>0;cx--) { *edi = *esi; esi+=Brosse_Largeur; edi++; } Debut_de_colonne--; } } // Remplacer une couleur par une autre dans un buffer void Remap_general_LOWLEVEL(byte * Table_conv,byte * Buffer,short Largeur,short Hauteur,short Largeur_buffer) { int dx,cx; // Pour chaque ligne for(dx=Hauteur;dx>0;dx--) { // Pour chaque pixel for(cx=Largeur;cx>0;cx--) { *Buffer = Table_conv[*Buffer]; Buffer++; } Buffer += Largeur_buffer-Largeur; } } void Copier_image_dans_brosse(short Debut_X,short Debut_Y,short Brosse_Largeur,short Brosse_Hauteur,word Largeur_image) { byte* Src=Debut_Y*Largeur_image+Debut_X+Principal_Ecran; //Adr départ image (ESI) byte* Dest=Brosse; //Adr dest brosse (EDI) int dx; for (dx=Brosse_Hauteur;dx!=0;dx--) //Pour chaque ligne { // On fait une copie de la ligne memcpy(Dest,Src,Brosse_Largeur); // On passe à la ligne suivante Src+=Largeur_image; Dest+=Brosse_Largeur; } } byte Lit_pixel_dans_ecran_feedback (word X,word Y) { return *(FX_Feedback_Ecran+Y*Principal_Largeur_image+X); } dword Round_div(dword Numerateur,dword Diviseur) { return Numerateur/Diviseur; } byte Effet_Trame(word X,word Y) { return Trame[X % Trame_Largeur][Y % Trame_Hauteur]; } void Set_mouse_position(void) { SDL_WarpMouse( Mouse_X*Pixel_width, Mouse_Y*Pixel_height ); } void Remplacer_toutes_les_couleurs_dans_limites(byte * Table_de_remplacement) { int Ligne; int Compteur; byte* Adresse; byte Ancien; // Pour chaque ligne : for(Ligne = Limite_Haut;Ligne <= Limite_Bas; Ligne++) { // Pour chaque pixel sur la ligne : for (Compteur = Limite_Gauche;Compteur <= Limite_Droite;Compteur ++) { Adresse = Principal_Ecran+Ligne*Principal_Largeur_image+Compteur; Ancien=*Adresse; *Adresse = Table_de_remplacement[Ancien]; } } } byte Lit_pixel_dans_ecran_backup (word X,word Y) { return *(Ecran_backup + X + Principal_Largeur_image * Y); } // Les images ILBM sont stockés en bitplanes donc on doit trifouiller les bits pour // en faire du chunky byte Couleur_ILBM_line(word Pos_X, word Vraie_taille_ligne, byte HBPm1) { // CL sera le rang auquel on extrait les bits de la couleur byte cl = 7 - (Pos_X & 7); int ax,bh,dx; byte bl=0; for(dx=HBPm1;dx>=0;dx--) { //CIL_Loop ax = (Vraie_taille_ligne * dx + Pos_X) >> 3; bh = (LBM_Buffer[ax] >> cl) & 1; bl = (bl << 1) + bh; } return bl; } void Palette_256_to_64(T_Palette Palette) { int i; for(i=0;i<256;i++) { Palette[i].R = Palette[i].R >> 2; Palette[i].V = Palette[i].V >> 2; Palette[i].B = Palette[i].B >> 2; } } void Palette_64_to_256(T_Palette Palette) { int i; for(i=0;i<256;i++) { Palette[i].R = (Palette[i].R << 2)|(Palette[i].R >> 4); Palette[i].V = (Palette[i].V << 2)|(Palette[i].V >> 4); Palette[i].B = (Palette[i].B << 2)|(Palette[i].B >> 4); } } byte Effet_Colorize_interpole (word X,word Y,byte Couleur) { // Facteur_A = 256*(100-Colorize_Opacite)/100 // Facteur_B = 256*( Colorize_Opacite)/100 // // (Couleur_dessous*Facteur_A+Couleur*facteur_B)/256 // // On place dans ESI 3*Couleur_dessous ( = position de cette couleur dans la // palette des teintes) et dans EDI, 3*Couleur. byte Bleu_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].B; byte Bleu=Principal_Palette[Couleur].B; byte Vert_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].V; byte Vert=Principal_Palette[Couleur].V; byte Rouge_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].R; byte Rouge=Principal_Palette[Couleur].R; // On récupère les 3 composantes RVB // Bleu Bleu = (Table_de_multiplication_par_Facteur_B[Bleu] + Table_de_multiplication_par_Facteur_A[Bleu_dessous]) / 256; Vert = (Table_de_multiplication_par_Facteur_B[Vert] + Table_de_multiplication_par_Facteur_A[Vert_dessous]) / 256; Rouge = (Table_de_multiplication_par_Facteur_B[Rouge] + Table_de_multiplication_par_Facteur_A[Rouge_dessous]) / 256; return Meilleure_couleur(Rouge,Vert,Bleu); } byte Effet_Colorize_additif (word X,word Y,byte Couleur) { byte Bleu_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].B; byte Vert_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].V; byte Rouge_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].R; byte Bleu=Principal_Palette[Couleur].B; byte Vert=Principal_Palette[Couleur].V; byte Rouge=Principal_Palette[Couleur].R; return Meilleure_couleur( Rouge>Rouge_dessous?Rouge:Rouge_dessous, Vert>Vert_dessous?Vert:Vert_dessous, Bleu>Bleu_dessous?Bleu:Bleu_dessous); } byte Effet_Colorize_soustractif(word X,word Y,byte Couleur) { byte Bleu_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].B; byte Vert_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].V; byte Rouge_dessous=Principal_Palette[*(FX_Feedback_Ecran + Y * Principal_Largeur_image + X)].R; byte Bleu=Principal_Palette[Couleur].B; byte Vert=Principal_Palette[Couleur].V; byte Rouge=Principal_Palette[Couleur].R; return Meilleure_couleur( RougeChrono_cmp) Etat_chrono=1; } // Effectue une inversion de la brosse selon une droite horizontale void Flip_Y_LOWLEVEL(void) { // ESI pointe sur la partie haute de la brosse // EDI sur la partie basse byte* ESI = Brosse ; byte* EDI = Brosse + (Brosse_Hauteur - 1) *Brosse_Largeur; byte tmp; word cx; while(ESI < EDI) { // Il faut inverser les lignes pointées par ESI et // EDI ("Brosse_Largeur" octets en tout) for(cx = Brosse_Largeur;cx>0;cx--) { tmp = *ESI; *ESI = *EDI; *EDI = tmp; ESI++; EDI++; } // On change de ligne : // ESI pointe déjà sur le début de la ligne suivante // EDI pointe sur la fin de la ligne en cours, il // doit pointer sur le début de la précédente... EDI -= 2 * Brosse_Largeur; // On recule de 2 lignes } } // Effectue une inversion de la brosse selon une droite verticale void Flip_X_LOWLEVEL(void) { // ESI pointe sur la partie gauche et EDI sur la partie // droite byte* ESI = Brosse; byte* EDI = Brosse + Brosse_Largeur - 1; byte* Debut_Ligne; byte* Fin_Ligne; byte tmp; word cx; while(ESI0;cx--) { tmp=*ESI; *ESI=*EDI; *EDI=tmp; EDI+=Brosse_Largeur; ESI+=Brosse_Largeur; } // On change de colonne // ESI > colonne suivante // EDI > colonne précédente ESI = Debut_Ligne + 1; EDI = Fin_Ligne - 1; } } // Faire une rotation de 180º de la brosse void Rotate_180_deg_LOWLEVEL(void) { // ESI pointe sur la partie supérieure de la brosse // EDI pointe sur la partie basse byte* ESI = Brosse; byte* EDI = Brosse + Brosse_Hauteur*Brosse_Largeur - 1; // EDI pointe sur le dernier pixel de la derniere ligne byte tmp; word cx; while(ESI < EDI) { // On échange les deux lignes pointées par EDI et // ESI (Brosse_Largeur octets) // En même temps, on échange les pixels, donc EDI // pointe sur la FIN de sa ligne for(cx=Brosse_Largeur;cx>0;cx--) { tmp = *ESI; *ESI = *EDI; *EDI = tmp; EDI--; // Attention ici on recule ! ESI++; } } } void Tempo_jauge(byte Vitesse) //Boucle d'attente pour faire bouger les scrollbars à une vitesse correcte { while (Vitesse!=0) { Wait_VBL(); Vitesse--; } } void Scroll_picture(short Decalage_X,short Decalage_Y) { byte* esi = Ecran_backup; //Source de la copie byte* edi = Principal_Ecran + Decalage_Y * Principal_Largeur_image + Decalage_X; const word ax = Principal_Largeur_image - Decalage_X; // Nombre de pixels à copier à droite word dx; for(dx = Principal_Hauteur_image - Decalage_Y;dx>0;dx--) { // Pour chaque ligne memcpy(edi,esi,ax); memcpy(edi - Decalage_X,esi+ax,Decalage_X); // On passe à la ligne suivante edi += Principal_Largeur_image; esi += Principal_Largeur_image; } // On vient de faire le traitement pour otutes les lignes au-dessous de Decalage_Y // Maintenant on traite celles au dessus edi = Decalage_X + Principal_Ecran; for(dx = Decalage_Y;dx>0;dx--) { memcpy(edi,esi,ax); memcpy(edi - Decalage_X,esi+ax,Decalage_X); edi += Principal_Largeur_image; esi += Principal_Largeur_image; } UpdateRect(0,0,0,0); } word Get_key(void) { SDL_Event event; Attendre_fin_de_click(); // On prend le controle de la boucle d'évènements, donc il ne faut pas qu'on rate la fin de click ! while(1) { SDL_WaitEvent(&event); if(event.type == SDL_KEYDOWN) { return Conversion_ANSI(event.key.keysym); } else Gere_Evenement_SDL(&event); } } void Zoomer_une_ligne(byte* Ligne_originale, byte* Ligne_zoomee, word Facteur, word Largeur ) { byte couleur; word larg; // Pour chaque pixel for(larg=0;larg #elif defined(__macosx__) #include #elif defined(__BEOS__) || defined(__HAIKU__) // sysinfo not implemented #elif defined(__AROS__) #include #else #include // sysinfo() for free RAM #endif // Indique quelle est la mémoire disponible unsigned long Memoire_libre(void) { // On appelle la fonction qui optimise la mémoire libre afin d'en // regagner un maximum. Sinon, tous les "free" libèrent une mémoire qui // n'est pas prise en compte par la fonction, et on se retrouve avec un // manque alarmant de mémoire. /* A revoir, mais est-ce vraiment utile? _heapmin(); */ // Memory is no longer relevant. If there is ANY problem or doubt here, // you can simply return 10*1024*1024 (10Mb), to make the "Pages"something // memory allocation functions happy. #if defined(__WIN32__) MEMORYSTATUSEX mstt; mstt.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&mstt); return mstt.ullAvailPhys; #elif defined(__macosx__) int mib[2]; int maxmem; size_t len; mib[0] = CTL_HW; mib[1] = HW_USERMEM; len = sizeof(maxmem); sysctl(mib,2,&maxmem,&len,NULL,0); return maxmem; #elif defined(__BEOS__) || defined(__HAIKU__) // No on BeOS or Haiku return 10*1024*1024; #elif defined(__AROS__) return AvailMem(MEMF_ANY); #else struct sysinfo info; sysinfo(&info); return info.freeram*info.mem_unit; #endif } // Transformer un nombre (entier naturel) en chaîne void Num2str(dword Nombre,char * Chaine,byte Taille) { int Indice; for (Indice=Taille-1;Indice>=0;Indice--) { Chaine[Indice]=(Nombre%10)+'0'; Nombre/=10; if (Nombre==0) for (Indice--;Indice>=0;Indice--) Chaine[Indice]=' '; } Chaine[Taille]='\0'; } // Transformer une chaîne en un entier naturel (renvoie -1 si ch. invalide) int Str2num(char * Chaine) { int Valeur=0; for (;*Chaine;Chaine++) { if ( (*Chaine>='0') && (*Chaine<='9') ) Valeur=(Valeur*10)+(*Chaine-'0'); else return -1; } return Valeur; } // Arrondir un nombre réel à la valeur entière la plus proche short Round(float Valeur) { short Temp=Valeur; if (Valeur>=0) { if ((Valeur-Temp)>= 0.5) Temp++; } else { if ((Valeur-Temp)<=-0.5) Temp--; } return Temp; } // Arrondir le résultat d'une division à la valeur entière supérieure short Round_div_max(short Numerateur,short Diviseur) { if (!(Numerateur % Diviseur)) return (Numerateur/Diviseur); else return (Numerateur/Diviseur)+1; } // Retourne le minimum entre deux nombres int Min(int A,int B) { return (AB)?A:B; } // Fonction retournant le libellé d'une mode (ex: " 320x200") char * Libelle_mode(int Mode) { static char Chaine[24]; if (! Mode_video[Mode].Fullscreen) return "window"; sprintf(Chaine, "%dx%d", Mode_video[Mode].Largeur, Mode_video[Mode].Hauteur); return Chaine; } // Trouve un mode video à partir d'une chaine: soit "window", // soit de la forme "320x200" // Renvoie -1 si la chaine n'est pas convertible int Conversion_argument_mode(const char *Argument) { // Je suis paresseux alors je vais juste tester les libellés int Indice_mode; for (Indice_mode=0; Indice_mode