/*  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 
*/
#include 
#include 
#include 
#include 
#include "struct.h"
#include "sdlscreen.h"
#include "global.h"
#include "errors.h"
#include "buttons.h"
#include "engine.h"
#include "misc.h"
#include "keyboard.h"
#include "sdlscreen.h"
#include "windows.h"
#include "palette.h"
#include "input.h"
word Count_used_colors(dword* usage)
{
  int nb_pixels=0;
  Uint8* current_pixel=Main_screen;
  Uint8 color;
  word nb_colors=0;
  int i;
  for (i=0;i<256;i++) usage[i]=0;
  //Calcul du nombre de pixels dans l'image
  nb_pixels=Main_image_height*Main_image_width;
  // On parcourt l'écran courant pour compter les utilisations des couleurs
  for(i=0;i0;dx--)
  {
    // Pour chaque pixel
    for(cx=width;cx>0;cx--)
    {
    *buffer = conversion_table[*buffer];
    buffer++;
    }
    buffer += buffer_width-width;
  }
}
void Copy_image_to_brush(short start_x,short start_y,short Brush_width,short Brush_height,word image_width)
{
    byte* src=start_y*image_width+start_x+Main_screen; //Adr départ image (ESI)
    byte* dest=Brush; //Adr dest brosse (EDI)
    int dx;
  for (dx=Brush_height;dx!=0;dx--)
  //Pour chaque ligne
  {
    // On fait une copie de la ligne
    memcpy(dest,src,Brush_width);
    // On passe à la ligne suivante
    src+=image_width;
    dest+=Brush_width;
   }
}
byte Read_pixel_from_feedback_screen (word x,word y)
{
  return *(FX_feedback_screen+y*Main_image_width+x);
}
dword Round_div(dword numerator,dword divisor)
{
        return numerator/divisor;
}
byte Effect_sieve(word x,word y)
{
  return Sieve[x % Sieve_width][y % Sieve_height];
}
void Set_mouse_position(void)
{
    SDL_WarpMouse(
        Mouse_X*Pixel_width,
        Mouse_Y*Pixel_height
    );
}
void Replace_colors_within_limits(byte * replace_table)
{
  int line;
  int counter;
  byte* Adresse;
  byte old;
  // Pour chaque ligne :
  for(line = Limit_top;line <= Limit_bottom; line++)
  {
    // Pour chaque pixel sur la ligne :
    for (counter = Limit_left;counter <= Limit_right;counter ++)
    {
      Adresse = Main_screen+line*Main_image_width+counter;
      old=*Adresse;
      *Adresse = replace_table[old];
    }
  }
}
byte Read_pixel_from_backup_screen (word x,word y)
{
  return *(Screen_backup + x + Main_image_width * y);
}
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].G = palette[i].G >> 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].G = (palette[i].G << 2)|(palette[i].G >> 4);
    palette[i].B = (palette[i].B << 2)|(palette[i].B >> 4);
  }
}
byte Effect_interpolated_colorize  (word x,word y,byte color)
{
  // factor_a = 256*(100-Colorize_opacity)/100
  // factor_b = 256*(    Colorize_opacity)/100
  //
  // (Couleur_dessous*factor_a+color*facteur_B)/256
  //
  // On place dans ESI 3*Couleur_dessous ( = position de cette couleur dans la
  // palette des teintes) et dans EDI, 3*color.
  byte blue_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].B;
  byte blue=Main_palette[color].B;
  byte green_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].G;
  byte green=Main_palette[color].G;
  byte red_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].R;
  byte red=Main_palette[color].R;
  // On récupère les 3 composantes RVB
  // blue
  blue = (Factors_inv_table[blue]
    + Factors_table[blue_under]) / 256;
  green = (Factors_inv_table[green]
    + Factors_table[green_under]) / 256;
  red = (Factors_inv_table[red]
    + Factors_table[red_under]) / 256;
  return Best_color(red,green,blue);
}
byte Effect_additive_colorize    (word x,word y,byte color)
{
  byte blue_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].B;
  byte green_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].G;
  byte red_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].R;
  byte blue=Main_palette[color].B;
  byte green=Main_palette[color].G;
  byte red=Main_palette[color].R;
  return Best_color(
    red>red_under?red:red_under,
    green>green_under?green:green_under,
    blue>blue_under?blue:blue_under);
}
byte Effect_substractive_colorize(word x,word y,byte color)
{
  byte blue_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].B;
  byte green_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].G;
  byte red_under=Main_palette[*(FX_feedback_screen + y * Main_image_width + x)].R;
  byte blue=Main_palette[color].B;
  byte green=Main_palette[color].G;
  byte red=Main_palette[color].R;
  return Best_color(
    redTimer_start) Timer_state=1;
}
void Flip_Y_lowlevel(byte *src, short width, short height)
{
  // ESI pointe sur la partie haute de la brosse
  // EDI sur la partie basse
  byte* ESI = src ;
  byte* EDI = src + (height - 1) *width;
  byte tmp;
  word cx;
  while(ESI < EDI)
  {
    // Il faut inverser les lignes pointées par ESI et
    // EDI ("Brush_width" octets en tout)
    for(cx = width;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 * width; // On recule de 2 lignes
  }
}
void Flip_X_lowlevel(byte *src, short width, short height)
{
  // ESI pointe sur la partie gauche et EDI sur la partie
  // droite
  byte* ESI = src;
  byte* EDI = src + width - 1;
  byte* line_start;
  byte* line_end;
  byte tmp;
  word cx;
  while(ESI0;cx--)
    {
      tmp=*ESI;
      *ESI=*EDI;
      *EDI=tmp;
      EDI+=width;
      ESI+=width;
    }
    // On change de colonne
    // ESI > colonne suivante
    // EDI > colonne précédente
    ESI = line_start + 1;
    EDI = line_end - 1;
  }
}
// Rotate a pixel buffer 180º on itself.
void Rotate_180_deg_lowlevel(byte *src, short width, short height)
{
  // ESI pointe sur la partie supérieure de la brosse
  // EDI pointe sur la partie basse
  byte* ESI = src;
  byte* EDI = src + height*width - 1;
  // EDI pointe sur le dernier pixel de la derniere ligne
  byte tmp;
  word cx;
  // In case of odd height, the algorithm in this function would
  // miss the middle line, so we do it this way:
  if (height & 1)
  {
    Flip_X_lowlevel(src, width, height);
    Flip_Y_lowlevel(src, width, height);
    return;
  }
  while(ESI < EDI)
  {
    // On échange les deux lignes pointées par EDI et
    // ESI (Brush_width octets)
    // En même temps, on échange les pixels, donc EDI
    // pointe sur la FIN de sa ligne
    for(cx=width;cx>0;cx--)
    {
      tmp = *ESI;
      *ESI = *EDI;
      *EDI = tmp;
      EDI--; // Attention ici on recule !
      ESI++;
    }
  }
}
void Rescale(byte *src_buffer, short src_width, short src_height, byte *dst_buffer, short dst_width, short dst_height, short x_flipped, short y_flipped)
{
  int    offset,line,column;
  int    x_pos_in_brush;   // Position courante dans l'ancienne brosse
  int    y_pos_in_brush;
  int    delta_x_in_brush; // "Vecteur incrémental" du point précédent
  int    delta_y_in_brush;
  int    initial_x_pos;       // Position X de début de parcours de ligne
  
  // Calcul du "vecteur incrémental":
  delta_x_in_brush=(src_width<<16) * (x_flipped?-1:1) / (dst_width);
  delta_y_in_brush=(src_height<<16) * (y_flipped?-1:1) / (dst_height);
  offset=0;
  // Calcul de la valeur initiale de y_pos:
  if (y_flipped)
    y_pos_in_brush=(src_height<<16)-1; // Inversion en Y de la brosse
  else
    y_pos_in_brush=0;                // Pas d'inversion en Y de la brosse
  // Calcul de la valeur initiale de x_pos pour chaque ligne:
  if (x_flipped)
    initial_x_pos = (src_width<<16)-1; // Inversion en X de la brosse
  else
    initial_x_pos = 0;                // Pas d'inversion en X de la brosse
  
  // Pour chaque ligne
  for (line=0;line>16) + (y_pos_in_brush>>16)*src_width);
      // On passe à la colonne de brosse suivante:
      x_pos_in_brush+=delta_x_in_brush;
      // On passe au pixel suivant de la nouvelle brosse:
      offset++;
    }
    // On passe à la ligne de brosse suivante:
    y_pos_in_brush+=delta_y_in_brush;
  }
}
void Slider_timer(byte speed)
//Boucle d'attente pour faire bouger les scrollbars à une vitesse correcte
{
  Uint32 end;
  byte original_mouse_k = Mouse_K;
  end = SDL_GetTicks() + speed*10;
  do
  {
    if (!Get_input()) Wait_VBL();
  } while (Mouse_K == original_mouse_k && SDL_GetTicks()0;dx--)
  {
  // Pour chaque ligne
    memcpy(edi,esi,ax);
    memcpy(edi - x_offset,esi+ax,x_offset);
    // On passe à la ligne suivante
    edi += Main_image_width;
    esi += Main_image_width;
  }
  // On vient de faire le traitement pour otutes les lignes au-dessous de y_offset
  // Maintenant on traite celles au dessus
  edi = x_offset + Main_screen;
  for(dx = y_offset;dx>0;dx--)
  {
    memcpy(edi,esi,ax);
    memcpy(edi - x_offset,esi+ax,x_offset);
    edi += Main_image_width;
    esi += Main_image_width;
  }
  Update_rect(0,0,0,0);
}
void Zoom_a_line(byte* original_line, byte* zoomed_line,
        word factor, word width
)
{
        byte color;
        word x;
        // Pour chaque pixel
        for(x=0;x
#elif defined(__macosx__) || defined(__FreeBSD__)
    #include 
#elif defined(__BEOS__) || defined(__HAIKU__)
    // sysinfo not implemented
#elif defined(__AROS__) || defined(__amigaos4__) || defined(__MORPHOS__) || defined(__amigaos__)
    #include 
#elif defined(__SKYOS__)
    #include 
#else
    #include  // sysinfo() for free RAM
#endif
// Indique quelle est la mémoire disponible
unsigned long Memory_free(void)
{
    // 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.
    // However, it is still a good idea to make a proper function if you can...
    // If Grafx2 thinks the memory is full, weird things may happen. And if memory
    // ever becomes full and you're still saying there are 10MB free here, the
    // program will crash without saving any picture backup ! You've been warned...
    #if defined(__WIN32__)
        MEMORYSTATUS mstt;
        mstt.dwLength = sizeof(MEMORYSTATUS);
        GlobalMemoryStatus(&mstt);
        return mstt.dwAvailPhys;
    #elif defined(__macosx__) || defined(__FreeBSD__)
        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__) || defined(__SKYOS__) || defined(__amigaos4__) || defined(__amigaos__)
        // No  on BeOS or Haiku
        // AvailMem is misleading on os4 (os4 caches stuff in memory that you can still allocate)
        #warning "There is missing code there for your platform ! please check and correct :)"
        return 10*1024*1024;
    #elif defined(__AROS__) || defined(__MORPHOS__)
        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 number,char * str,byte nb_char)
{
  int index;
  for (index=nb_char-1;index>=0;index--)
  {
    str[index]=(number%10)+'0';
    number/=10;
    if (number==0)
      for (index--;index>=0;index--)
        str[index]=' ';
  }
  str[nb_char]='\0';
}
// Arrondir un nombre réel à la valeur entière la plus proche
short Round(float value)
{
  short temp=value;
  if (value>=0)
    { if ((value-temp)>= 0.5) temp++; }
  else
    { if ((value-temp)<=-0.5) temp--; }
  return temp;
}
// Arrondir le résultat d'une division à la valeur entière supérieure
short Round_div_max(short numerator,short divisor)
{
  if (!(numerator % divisor))
    return (numerator/divisor);
  else
    return (numerator/divisor)+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 * Mode_label(int mode)
{
  static char str[24];
  if (! Video_mode[mode].Fullscreen)
    return "window";
  sprintf(str, "%dx%d", Video_mode[mode].Width, Video_mode[mode].Height);
  return str;
}
// 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 Convert_videomode_arg(const char *argument)
{
  // Je suis paresseux alors je vais juste tester les libellés
  int mode_index;
  for (mode_index=0; mode_index