/*  Grafx2 - The Ultimate 256-color bitmap paint program
    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 
#include 
#include 
#include "op_c.h"
#include "errors.h"
void RGB_to_HSL(int r,int g,int b,byte * hr,byte * sr,byte* lr)
{
  double rd,gd,bd,h,s,l,max,min;
  // convert RGB to HSV
  rd = r / 255.0;            // rd,gd,bd range 0-1 instead of 0-255
  gd = g / 255.0;
  bd = b / 255.0;
  // compute L
//  l=(rd*0.30)+(gd*0.59)+(bd*0.11);
  // compute maximum of rd,gd,bd
  if (rd>=gd)
  {
    if (rd>=bd)
      max = rd;
    else
      max = bd;
  }
  else
  {
    if (gd>=bd)
      max = gd;
    else
      max = bd;
  }
  // compute minimum of rd,gd,bd
  if (rd<=gd)
  {
    if (rd<=bd)
      min = rd;
    else
      min = bd;
  }
  else
  {
    if (gd<=bd)
      min = gd;
    else
      min = bd;
  }
  l = (max + min) / 2.0;
  if(max==min)
      s = h = 0;
  else
  {
    if (l<=0.5)
        s = (max - min) / (max + min);
    else
        s = (max - min) / (2 - (max + min));
    if (max == rd)
        h = 42.5 * (gd-bd)/(max-min);
    else if (max == gd)
        h = 42.5 * (bd-rd)/(max-min)+85;
    else
        h = 42.5 * (rd-gd)/(max-min)+170;
    if (h<0) h+=255;
  }
  *hr = h;
  *lr = (l*255.0);
  *sr = (s*255.0);
}
void HSL_to_RGB(byte h,byte s,byte l, byte* r, byte* g, byte* b)
{
    float rf =0 ,gf = 0,bf = 0;
    float hf,lf,sf;
    float p,q;
    if(s==0)
    {
        *r=*g=*b=l;
        return;
    }
    hf = h / 255.0;
    lf = l / 255.0;
    sf = s / 255.0;
    if (lf<=0.5)
        q = lf*(1+sf);
    else
        q = lf+sf-lf*sf;
    p = 2*lf-q;
    rf = hf + (1 / 3.0);
    gf = hf;
    bf = hf - (1 / 3.0);
    if (rf < 0) rf+=1;
    if (rf > 1) rf-=1;
    if (gf < 0) gf+=1;
    if (gf > 1) gf-=1;
    if (bf < 0) bf+=1;
    if (bf > 1) bf-=1;
    if (rf < 1/6.0)
        rf = p + ((q-p)*6*rf);
    else if(rf < 0.5)
        rf = q;
    else if(rf < 2/3.0)
        rf = p + ((q-p)*6*(2/3.0-rf));
    else
        rf = p;
    if (gf < 1/6.0)
        gf = p + ((q-p)*6*gf);
    else if(gf < 0.5)
        gf = q;
    else if(gf < 2/3.0)
        gf = p + ((q-p)*6*(2/3.0-gf));
    else
        gf = p;
    if (bf < 1/6.0)
        bf = p + ((q-p)*6*bf);
    else if(bf < 0.5)
        bf = q;
    else if(bf < 2/3.0)
        bf = p + ((q-p)*6*(2/3.0-bf));
    else
        bf = p;
    *r = rf * (255);
    *g = gf * (255);
    *b = bf * (255);
}
/////////////////////////////////////////////////////////////////////////////
///////////////////////////// Méthodes de gestion des tables de conversion //
/////////////////////////////////////////////////////////////////////////////
T_Conversion_table * CT_new(int nbb_r,int nbb_g,int nbb_b)
{
  T_Conversion_table * n;
  int size;
  n=(T_Conversion_table *)malloc(sizeof(T_Conversion_table));
  if (n!=NULL)
  {
    // On recopie les paramŠtres demand‚s
    n->nbb_r=nbb_r;
    n->nbb_g=nbb_g;
    n->nbb_b=nbb_b;
    // On calcule les autres
    n->rng_r=(1<rng_g=(1<rng_b=(1<dec_r=nbb_g+nbb_b;
    n->dec_g=nbb_b;
    n->dec_b=0;
    n->red_r=8-nbb_r;
    n->red_g=8-nbb_g;
    n->red_b=8-nbb_b;
    // On tente d'allouer la table
    size=(n->rng_r)*(n->rng_g)*(n->rng_b);
    n->table=(byte *)malloc(size);
    if (n->table!=NULL)
      // C'est bon!
      memset(n->table,0,size); // Inutile, mais plus propre
    else
    {
      // Table impossible … allouer
      free(n);
      n=NULL;
    }
  }
  return n;
}
void CT_delete(T_Conversion_table * t)
{
  free(t->table);
  free(t);
}
byte CT_get(T_Conversion_table * t,int r,int g,int b)
{
  int index;
  // On réduit le nombre de bits par couleur
  r=(r>>t->red_r);
  g=(g>>t->red_g);
  b=(b>>t->red_b);
  
  // On recherche la couleur la plus proche dans la table de conversion
  index=(r<dec_r) | (g<dec_g) | (b<dec_b);
  return t->table[index];
}
void CT_set(T_Conversion_table * t,int r,int g,int b,byte i)
{
  int index;
  index=(r<dec_r) | (g<dec_g) | (b<dec_b);
  t->table[index]=i;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////// M‚thodes de gestion des tables d'occurence //
/////////////////////////////////////////////////////////////////////////////
void OT_init(T_Occurrence_table * t)
{
  int size;
  size=(t->rng_r)*(t->rng_g)*(t->rng_b)*sizeof(int);
  memset(t->table,0,size); // On initialise … 0
}
T_Occurrence_table * OT_new(int nbb_r,int nbb_g,int nbb_b)
{
  T_Occurrence_table * n;
  int size;
  n=(T_Occurrence_table *)malloc(sizeof(T_Occurrence_table));
  if (n!=0)
  {
    // On recopie les paramŠtres demand‚s
    n->nbb_r=nbb_r;
    n->nbb_g=nbb_g;
    n->nbb_b=nbb_b;
    // On calcule les autres
    n->rng_r=(1<rng_g=(1<rng_b=(1<dec_r=nbb_g+nbb_b;
    n->dec_g=nbb_b;
    n->dec_b=0;
    n->red_r=8-nbb_r;
    n->red_g=8-nbb_g;
    n->red_b=8-nbb_b;
    // On tente d'allouer la table
    size=(n->rng_r)*(n->rng_g)*(n->rng_b)*sizeof(int);
    n->table=(int *)malloc(size);
    if (n->table!=0)
      // C'est bon! On initialise … 0
      OT_init(n);
    else
    {
      // Table impossible … allouer
      free(n);
      n=0;
    }
  }
  return n;
}
void OT_delete(T_Occurrence_table * t)
{
  free(t->table);
  free(t);
}
int OT_get(T_Occurrence_table * t,int r,int g,int b)
{
  int index;
  index=(r<dec_r) | (g<dec_g) | (b<dec_b);
  return t->table[index];
}
void OT_inc(T_Occurrence_table * t,int r,int g,int b)
{
  int index;
  r=(r>>t->red_r);
  g=(g>>t->red_g);
  b=(b>>t->red_b);
  index=(r<dec_r) | (g<dec_g) | (b<dec_b);
  t->table[index]++;
}
void OT_count_occurrences(T_Occurrence_table * t,T_Bitmap24B image,int size)
{
  T_Bitmap24B ptr;
  int index;
  for (index=size,ptr=image;index>0;index--,ptr++)
    OT_inc(t,ptr->R,ptr->G,ptr->B);
}
int OT_count_colors(T_Occurrence_table * t)
{
  int val; // Valeur de retour
  int nb;  // Nombre de couleurs … tester
  int i;   // Compteur de couleurs test‚es
  val=0;
  nb=(t->rng_r)*(t->rng_g)*(t->rng_b);
  for (i=0;itable[i]>0)
      val++;
  return val;
}
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// M‚thodes de gestion des clusters //
/////////////////////////////////////////////////////////////////////////////
void Cluster_analyser(T_Cluster * c,T_Occurrence_table * to)
{
  int rmin,rmax,vmin,vmax,bmin,bmax;
  int r,g,b;
  // On cherche les mins et les maxs de chaque composante sur la couverture
  // int nbocc;
  // On prédécale tout pour éviter de faire trop de bazar en se forçant à utiliser OT_get, plus rapide
  rmin=c->rmax <<16; rmax=c->rmin << 16;
  vmin=c->vmax << 8; vmax=c->vmin << 8;
  bmin=c->bmax; bmax=c->bmin;
  c->occurences=0;
  /*
  for (r=c->rmin<<16;r<=c->rmax<<16;r+=1<<16)
    for (g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
      for (b=c->bmin;b<=c->bmax;b++)
      {
        nbocc=to->table[r + g + b]; // OT_get
        if (nbocc)
        {
          if (rrmax) rmax=r;
          if (gvmax) vmax=g;
          if (bbmax) bmax=b;
          c->occurences+=nbocc;
        }
      }
  */
  // On recherche le minimum et le maximum en parcourant le cluster selon chaque composante, 
  // ça évite des accès mémoires inutiles, de plus chaque boucle est plus petite que la 
  // précédente puisqu'on connait une borne supplémentaire
  for(r=c->rmin<<16;r<=c->rmax<<16;r+=1<<16)
      for(g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
          for(b=c->bmin;b<=c->bmax;b++)
          {
            if(to->table[r + g + b]) // OT_get
            {
                rmin=r;
                goto RMAX;
            }
          }
RMAX:
  for(r=c->rmax<<16;r>=rmin;r-=1<<16)
      for(g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
          for(b=c->bmin;b<=c->bmax;b++)
          {
            if(to->table[r + g + b]) // OT_get
            {
                rmax=r;
                goto VMIN;
            }
          }
VMIN:
  for(g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
      for(r=rmin;r<=rmax;r+=1<<16)
          for(b=c->bmin;b<=c->bmax;b++)
          {
            if(to->table[r + g + b]) // OT_get
            {
                vmin=g;
                goto VMAX;
            }
          }
VMAX:
  for(g=c->vmax<<8;g>=vmin;g-=1<<8)
      for(r=rmin;r<=rmax;r+=1<<16)
          for(b=c->bmin;b<=c->bmax;b++)
          {
            if(to->table[r + g + b]) // OT_get
            {
                vmax=g;
                goto BMIN;
            }
          }
BMIN:
  for(b=c->bmin;b<=c->bmax;b++)
      for(r=rmin;r<=rmax;r+=1<<16)
          for(g=vmin;g<=vmax;g+=1<<8)
          {
            if(to->table[r + g + b]) // OT_get
            {
                bmin=b;
                goto BMAX;
            }
          }
BMAX:
  for(b=c->bmax;b>=bmin;b--)
      for(r=rmin;r<=rmax;r+=1<<16)
          for(g=vmin;g<=vmax;g+=1<<8)
          {
            if(to->table[r + g + b]) // OT_get
            {
                bmax=b;
                goto ENDCRUSH;
            }
          }
ENDCRUSH:
  // Il faut quand même parcourir la partie utile du cluster, pour savoir combien il y a d'occurences
  for(r=rmin;r<=rmax;r+=1<<16)
      for(g=vmin;g<=vmax;g+=1<<8)
          for(b=bmin;b<=bmax;b++)
          {
            c->occurences+=to->table[r + g + b]; // OT_get
          }
  c->rmin=rmin>>16; c->rmax=rmax>>16;
  c->vmin=vmin>>8;  c->vmax=vmax>>8;
  c->bmin=bmin;     c->bmax=bmax;
  // On regarde la composante qui a la variation la plus grande
  r=(c->rmax-c->rmin)*299;
  g=(c->vmax-c->vmin)*587;
  b=(c->bmax-c->bmin)*114;
  if (g>=r)
  {
    // G>=R
    if (g>=b)
    {
      // G>=R et G>=B
      c->plus_large=1;
    }
    else
    {
      // G>=R et Gplus_large=2;
    }
  }
  else
  {
    // R>G
    if (r>=b)
    {
      // R>G et R>=B
      c->plus_large=0;
    }
    else
    {
      // R>G et Rplus_large=2;
    }
  }
}
void Cluster_split(T_Cluster * c,T_Cluster * c1,T_Cluster * c2,int hue,T_Occurrence_table * to)
{
  int limit;
  int cumul;
  int r,g,b;
  limit=(c->occurences)/2;
  cumul=0;
  if (hue==0)
  {
    for (r=c->rmin<<16;r<=c->rmax<<16;r+=1<<16)
    {
      for (g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
      {
        for (b=c->bmin;b<=c->bmax;b++)
        {
          cumul+=to->table[r + g + b];
          if (cumul>=limit)
            break;
        }
        if (cumul>=limit)
          break;
      }
      if (cumul>=limit)
        break;
    }
    r>>=16;
    g>>=8;
    if (r==c->rmin)
      r++;
    // R est la valeur de d‚but du 2nd cluster
    c1->Rmin=c->Rmin; c1->Rmax=r-1;
    c1->rmin=c->rmin; c1->rmax=r-1;
    c1->Gmin=c->Gmin; c1->Vmax=c->Vmax;
    c1->vmin=c->vmin; c1->vmax=c->vmax;
    c1->Bmin=c->Bmin; c1->Bmax=c->Bmax;
    c1->bmin=c->bmin; c1->bmax=c->bmax;
    c2->Rmin=r;       c2->Rmax=c->Rmax;
    c2->rmin=r;       c2->rmax=c->rmax;
    c2->Gmin=c->Gmin; c2->Vmax=c->Vmax;
    c2->vmin=c->vmin; c2->vmax=c->vmax;
    c2->Bmin=c->Bmin; c2->Bmax=c->Bmax;
    c2->bmin=c->bmin; c2->bmax=c->bmax;
  }
  else
  if (hue==1)
  {
    for (g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
    {
      for (r=c->rmin<<16;r<=c->rmax<<16;r+=1<<16)
      {
        for (b=c->bmin;b<=c->bmax;b++)
        {
          cumul+=to->table[r + g + b];
          if (cumul>=limit)
            break;
        }
        if (cumul>=limit)
          break;
      }
      if (cumul>=limit)
        break;
    }
    r>>=16; g>>=8;
    if (g==c->vmin)
      g++;
    // G est la valeur de d‚but du 2nd cluster
    c1->Rmin=c->Rmin; c1->Rmax=c->Rmax;
    c1->rmin=c->rmin; c1->rmax=c->rmax;
    c1->Gmin=c->Gmin; c1->Vmax=g-1;
    c1->vmin=c->vmin; c1->vmax=g-1;
    c1->Bmin=c->Bmin; c1->Bmax=c->Bmax;
    c1->bmin=c->bmin; c1->bmax=c->bmax;
    c2->Rmin=c->Rmin; c2->Rmax=c->Rmax;
    c2->rmin=c->rmin; c2->rmax=c->rmax;
    c2->Gmin=g;       c2->Vmax=c->Vmax;
    c2->vmin=g;       c2->vmax=c->vmax;
    c2->Bmin=c->Bmin; c2->Bmax=c->Bmax;
    c2->bmin=c->bmin; c2->bmax=c->bmax;
  }
  else
  {
    for (b=c->bmin;b<=c->bmax;b++)
    {
      for (g=c->vmin<<8;g<=c->vmax<<8;g+=1<<8)
      {
        for (r=c->rmin<<16;r<=c->rmax<<16;r+=1<<16)
        {
          cumul+=to->table[r + g + b];
          if (cumul>=limit)
            break;
        }
        if (cumul>=limit)
          break;
      }
      if (cumul>=limit)
        break;
    }
    r>>=16; g>>=8;
    if (b==c->bmin)
      b++;
    // B est la valeur de d‚but du 2nd cluster
    c1->Rmin=c->Rmin; c1->Rmax=c->Rmax;
    c1->rmin=c->rmin; c1->rmax=c->rmax;
    c1->Gmin=c->Gmin; c1->Vmax=c->Vmax;
    c1->vmin=c->vmin; c1->vmax=c->vmax;
    c1->Bmin=c->Bmin; c1->Bmax=b-1;
    c1->bmin=c->bmin; c1->bmax=b-1;
    c2->Rmin=c->Rmin; c2->Rmax=c->Rmax;
    c2->rmin=c->rmin; c2->rmax=c->rmax;
    c2->Gmin=c->Gmin; c2->Vmax=c->Vmax;
    c2->vmin=c->vmin; c2->vmax=c->vmax;
    c2->Bmin=b;       c2->Bmax=c->Bmax;
    c2->bmin=b;       c2->bmax=c->bmax;
  }
}
void Cluster_compute_hue(T_Cluster * c,T_Occurrence_table * to)
{
  int cumul_r,cumul_g,cumul_b;
  int r,g,b;
  int nbocc;
  byte s=0;
  cumul_r=cumul_g=cumul_b=0;
  for (r=c->rmin;r<=c->rmax;r++)
    for (g=c->vmin;g<=c->vmax;g++)
      for (b=c->bmin;b<=c->bmax;b++)
      {
        nbocc=OT_get(to,r,g,b);
        if (nbocc)
        {
          cumul_r+=r*nbocc;
          cumul_g+=g*nbocc;
          cumul_b+=b*nbocc;
        }
      }
  
  c->r=(cumul_r<red_r)/c->occurences;
  c->g=(cumul_g<red_g)/c->occurences;
  c->b=(cumul_b<red_b)/c->occurences;
  RGB_to_HSL(c->r,c->g,c->b,&c->h,&s,&c->l);
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////// M‚thodes de gestion des ensembles de clusters //
/////////////////////////////////////////////////////////////////////////////
/// Setup the first cluster before we start the operations
void CS_Init(T_Cluster_set * cs,T_Occurrence_table * to)
{
  cs->clusters->Rmin = cs->clusters->rmin = 0;
  cs->clusters->Gmin = cs->clusters->vmin = 0;
  cs->clusters->Bmin = cs->clusters->bmin = 0;
  cs->clusters->Rmax = cs->clusters->rmax = to->rng_r-1;
  cs->clusters->Vmax = cs->clusters->vmax = to->rng_g-1;
  cs->clusters->Bmax = cs->clusters->bmax = to->rng_b-1;
  cs->clusters->next = NULL;
  Cluster_analyser(cs->clusters,to);
  cs->nb=1;
}
/// Allocate a new cluster set
T_Cluster_set * CS_New(int nbmax,T_Occurrence_table * to)
{
  T_Cluster_set * n;
  n=(T_Cluster_set *)malloc(sizeof(T_Cluster_set));
  if (n != NULL)
  {
    // On recopie les paramŠtres demand‚s
    n->nb_max=OT_count_colors(to);
    // On vient de compter le nombre de couleurs existantes, s'il est plus grand que 256 on limite à 256 
	// (nombre de couleurs voulu au final)
    if (n->nb_max>nbmax)
    {
      n->nb_max=nbmax;
    }
    // On tente d'allouer le premier cluster
    n->clusters=(T_Cluster *)malloc(sizeof(T_Cluster));
    if (n->clusters!=NULL)
      // C'est bon! On initialise
      CS_Init(n,to);
    else
    {
      // Table impossible … allouer
      free(n);
      n=0;
    }
  }
  return n;
}
/// Free a cluster set
void CS_Delete(T_Cluster_set * cs)
{
	T_Cluster* nxt;
	while(cs->clusters != NULL)
	{
		nxt = cs->clusters->next;
  		free(cs->clusters);
		cs->clusters = nxt;
	}
  	free(cs);
}
void CS_Get(T_Cluster_set * cs,T_Cluster * c)
{
  T_Cluster* current = cs->clusters;
  T_Cluster* prev = NULL;
  // Search a cluster with at least 2 distinct colors so we can split it
  do
  {
    if ( (current->rmin < current->rmax) ||
         (current->vmin < current->vmax) ||
         (current->bmin < current->bmax) )
      break;
	prev = current;
	
  } while((current = current -> next));
  // copy it to c
  *c = *current;
  // remove it from the list
  cs->nb--;
  if(prev)
	prev->next = current->next;
  else
	cs->clusters = current->next;
  free(current);
  current = NULL;
}
void CS_Set(T_Cluster_set * cs,T_Cluster * c)
{
  T_Cluster* current = cs->clusters;
  T_Cluster* prev = NULL;
  // Search the first cluster that is smaller than ours
  if(current != NULL) // don't search if the list is empty
  do
  {
    if (current->occurences < c->occurences)
		break;
	prev = current;
  } while((current = current -> next));
  // Now insert our cluster just before the one we found
  c -> next = current;
  current = malloc(sizeof(T_Cluster));
  *current = *c ;
  if(prev) prev -> next = current;
  else cs->clusters = current;
  cs -> nb++;
}
// Détermination de la meilleure palette en utilisant l'algo Median Cut :
// 1) On considère l'espace (R,G,B) comme 1 boîte
// 2) On cherche les extrêmes de la boîte en (R,G,B)
// 3) On trie les pixels de l'image selon l'axe le plus long parmi (R,G,B)
// 4) On coupe la boîte en deux au milieu, et on compacte pour que chaque bord corresponde bien à un pixel extreme
// 5) On recommence à couper selon le plus grand axe toutes boîtes confondues
// 6) On s'arrête quand on a le nombre de couleurs voulu
void CS_Generate(T_Cluster_set * cs,T_Occurrence_table * to)
{
  T_Cluster current;
  T_Cluster Nouveau1;
  T_Cluster Nouveau2;
  // Tant qu'on a moins de 256 clusters
  while (cs->nbnb_max)
  {
    // On récupère le plus grand cluster
    CS_Get(cs,¤t);
    // On le coupe en deux
    Cluster_split(¤t,&Nouveau1,&Nouveau2,current.plus_large,to);
    // On compacte ces deux nouveaux (il peut y avoir un espace entre l'endroit de la coupure et les premiers pixels du cluster)
    Cluster_analyser(&Nouveau1,to);
    Cluster_analyser(&Nouveau2,to);
    // On met ces deux nouveaux clusters dans le clusterSet... sauf s'ils sont vides
    if(Nouveau1.occurences>0)
        CS_Set(cs,&Nouveau1);
    if(Nouveau2.occurences>0)
        CS_Set(cs,&Nouveau2);
  }
}
void CS_Compute_colors(T_Cluster_set * cs,T_Occurrence_table * to)
{
  T_Cluster * c;
  for (c=cs->clusters;c!=NULL;c=c->next)
    Cluster_compute_hue(c,to);
}
void CS_Sort_by_chrominance(T_Cluster_set * cs)
{
	T_Cluster* nc;
	T_Cluster* prev = NULL;
	T_Cluster* place;
	T_Cluster* newlist = NULL;
	while((nc = cs->clusters))
	{
		// Remove the first cluster from the original list
		nc = cs->clusters;
		cs->clusters = cs->clusters->next;
		// Find his position in the new list
		for(place = newlist;place != NULL; place = place->next)
		{
			if(place->h > nc->h) break;
			prev = place;
		}
		// Chain it there
		nc->next = place;
		if(prev) prev->next = nc;
		else newlist = nc;
	}
	// Put the new list bavk in place
	cs->clusters = newlist;
}
void CS_Sort_by_luminance(T_Cluster_set * cs)
{
	T_Cluster* nc;
	T_Cluster* prev = NULL;
	T_Cluster* place;
	T_Cluster* newlist = NULL;
	while((nc = cs->clusters))
	{
		// Remove the first cluster from the original list
		nc = cs->clusters;
		cs->clusters = cs->clusters->next;
		// Find his position in the new list
		for(place = newlist;place != NULL; place = place->next)
		{
			if(place->l > nc->l) break;
			prev = place;
		}
		// Chain it there
		nc->next = place;
		if(prev) prev->next = nc;
		else newlist = nc;
	}
	// Put the new list bavk in place
	cs->clusters = newlist;
}
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,T_Conversion_table * tc,T_Components * palette)
{
  int index;
  int r,g,b;
  T_Cluster* current = cs->clusters;
  for (index=0;indexnb;index++)
  {
    palette[index].R=current->r;
    palette[index].G=current->g;
    palette[index].B=current->b;
    for (r=current->Rmin; r<=current->Rmax; r++)
      for (g=current->Gmin;g<=current->Vmax;g++)
        for (b=current->Bmin;b<=current->Bmax;b++)
          CT_set(tc,r,g,b,index);
	current = current->next;
  }
}
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// Méthodes de gestion des dégradés //
/////////////////////////////////////////////////////////////////////////////
void GS_Init(T_Gradient_set * ds,T_Cluster_set * cs)
{
    ds->gradients[0].nb_colors=1;
    ds->gradients[0].min=cs->clusters->h;
    ds->gradients[0].max=cs->clusters->h;
    ds->gradients[0].hue=cs->clusters->h;
    // Et hop : le 1er ensemble de d‚grad‚s est initialis‚
    ds->nb=1;
}
T_Gradient_set * GS_New(T_Cluster_set * cs)
{
    T_Gradient_set * n;
    n=(T_Gradient_set *)malloc(sizeof(T_Gradient_set));
    if (n!=NULL)
    {
        // On recopie les paramŠtres demand‚s
        n->nb_max=cs->nb_max;
        // On tente d'allouer la table
        n->gradients=(T_Gradient *)malloc((n->nb_max)*sizeof(T_Gradient));
        if (n->gradients!=0)
            // C'est bon! On initialise
            GS_Init(n,cs);
        else
        {
            // Table impossible … allouer
            free(n);
            n=0;
        }
    }
    return n;
}
void GS_Delete(T_Gradient_set * ds)
{
    free(ds->gradients);
    free(ds);
}
void GS_Generate(T_Gradient_set * ds,T_Cluster_set * cs)
{
    int id; // Les indexs de parcours des ensembles
    int best_gradient; // Meilleur d‚grad‚
    int best_diff; // Meilleure diff‚rence de chrominance
    int diff;  // difference de chrominance courante
	T_Cluster * current = cs->clusters;
    // Pour chacun des clusters … traiter
    do
	{
		// On recherche le d‚grad‚ le plus proche de la chrominance du cluster
		best_gradient=-1;
		best_diff=99999999;
		for (id=0;idnb;id++)
		{
			diff=abs(current->h - ds->gradients[id].hue);
			if ((best_diff>diff) && (diff<16))
			{
				best_gradient=id;
				best_diff=diff;
			}
		}
		// Si on a trouv‚ un d‚grad‚ dans lequel inclure le cluster
		if (best_gradient!=-1)
		{
			// On met … jour le d‚grad‚
			if (current->h < ds->gradients[best_gradient].min)
				ds->gradients[best_gradient].min=current->h;
			if (current->h > ds->gradients[best_gradient].max)
				ds->gradients[best_gradient].max=current->h;
			ds->gradients[best_gradient].hue=((ds->gradients[best_gradient].hue*
						ds->gradients[best_gradient].nb_colors)
					+current->h)
				/(ds->gradients[best_gradient].nb_colors+1);
			ds->gradients[best_gradient].nb_colors++;
		}
		else
		{
			// On cr‚e un nouveau d‚grad‚
			best_gradient=ds->nb;
			ds->gradients[best_gradient].nb_colors=1;
			ds->gradients[best_gradient].min=current->h;
			ds->gradients[best_gradient].max=current->h;
			ds->gradients[best_gradient].hue=current->h;
			ds->nb++;
		}
		current->h=best_gradient;
	} while((current = current->next));
	// On redistribue les valeurs dans les clusters
	current = cs -> clusters;
	do
		current->h=ds->gradients[current->h].hue;
	while((current = current ->next));
}
T_Conversion_table * Optimize_palette(T_Bitmap24B image,int size,T_Components * palette,int r,int g,int b)
{
  T_Occurrence_table  * to;
  T_Conversion_table * tc;
  T_Cluster_set       * cs;
  T_Gradient_set       * ds;
  // Création des éléments nécessaires au calcul de palette optimisée:
  to=0; tc=0; cs=0; ds=0;
  to=OT_new(r,g,b);
  if (to!=0)
  {
    tc=CT_new(r,g,b);
    if (tc!=0)
    {
      // Première étape : on compte les pixels de chaque couleur pour pouvoir trier là dessus
      OT_count_occurrences(to,image,size);
      cs=CS_New(256,to);
      if (cs!=0)
      {
        // C'est bon, on a pu tout allouer
        // On génère les clusters (avec l'algo du median cut)
        CS_Generate(cs,to);
        // On calcule la teinte de chaque pixel (Luminance et chrominance)
        CS_Compute_colors(cs,to);
        ds=GS_New(cs);
        if (ds!=0)
        {
          GS_Generate(ds,cs);
          GS_Delete(ds);
        }
        // Enfin on trie les clusters (donc les couleurs de la palette) dans un ordre sympa : par couleur, et par luminosité pour chaque couleur
        CS_Sort_by_luminance(cs);
        CS_Sort_by_chrominance(cs);
        // Enfin on génère la palette et la table de correspondance entre chaque couleur 24b et sa couleur palette associée.
        CS_Generate_color_table_and_palette(cs,tc,palette);
        CS_Delete(cs);
        OT_delete(to);
        return tc;
      }
      CT_delete(tc);
    }
    OT_delete(to);
  }
  // Si on arrive ici c'est que l'allocation n'a pas réussi,
  // l'appelant devra recommencer avec une précision plus faible (3 derniers paramètres)
  return 0;
}
int Modified_value(int value,int modif)
{
  value+=modif;
  if (value<0)
  {
    value=0;
  }
  else if (value>255)
  {
    value=255;
  }
  return value;
}
void Convert_24b_bitmap_to_256_Floyd_Steinberg(T_Bitmap256 dest,T_Bitmap24B source,int width,int height,T_Components * palette,T_Conversion_table * tc)
// Cette fonction dégrade au fur et à mesure le bitmap source, donc soit on ne
// s'en ressert pas, soit on passe à la fonction une copie de travail du
// bitmap original.
{
  T_Bitmap24B current;
  T_Bitmap24B c_plus1;
  T_Bitmap24B u_minus1;
  T_Bitmap24B next;
  T_Bitmap24B u_plus1;
  T_Bitmap256 d;
  int x_pos,y_pos;
  int red,green,blue;
  float e_red,e_green,e_blue;
  // On initialise les variables de parcours:
  current =source;      // Le pixel dont on s'occupe
  next =current+width; // Le pixel en dessous
  c_plus1 =current+1;   // Le pixel à droite
  u_minus1=next-1;   // Le pixel en bas à gauche
  u_plus1 =next+1;   // Le pixel en bas à droite
  d       =dest;
  // On parcours chaque pixel:
  for (y_pos=0;y_posR;
      green =current->G;
      blue =current->B;
      // Cherche la couleur correspondant dans la palette et la range dans l'image de destination
      *d=CT_get(tc,red,green,blue);
      // Puis on calcule pour chaque composante l'erreur dûe à l'approximation
      red-=palette[*d].R;
      green -=palette[*d].G;
      blue -=palette[*d].B;
      // Et dans chaque pixel voisin on propage l'erreur
      // A droite:
        e_red=(red*7)/16.0;
        e_green =(green *7)/16.0;
        e_blue =(blue *7)/16.0;
        if (x_pos+1R=Modified_value(c_plus1->R,e_red);
          c_plus1->G=Modified_value(c_plus1->G,e_green );
          c_plus1->B=Modified_value(c_plus1->B,e_blue );
        }
      // En bas à gauche:
      if (y_pos+10)
        {
          u_minus1->R=Modified_value(u_minus1->R,e_red);
          u_minus1->G=Modified_value(u_minus1->G,e_green );
          u_minus1->B=Modified_value(u_minus1->B,e_blue );
        }
      // En bas:
        e_red=(red*5/16.0);
        e_green =(green*5 /16.0);
        e_blue =(blue*5 /16.0);
        next->R=Modified_value(next->R,e_red);
        next->G=Modified_value(next->G,e_green );
        next->B=Modified_value(next->B,e_blue );
      // En bas à droite:
        if (x_pos+1R=Modified_value(u_plus1->R,e_red);
          u_plus1->G=Modified_value(u_plus1->G,e_green );
          u_plus1->B=Modified_value(u_plus1->B,e_blue );
        }
      }
      // On passe au pixel suivant :
      current++;
      c_plus1++;
      u_minus1++;
      next++;
      u_plus1++;
      d++;
    }
  }
}
static const byte precision_24b[]=
{
 8,8,8,
 6,6,6,
 6,6,5,
 5,6,5,
 5,5,5,
 5,5,4,
 4,5,4,
 4,4,4,
 4,4,3,
 3,4,3,
 3,3,3,
 3,3,2};
// Convertie avec le plus de précision possible une image 24b en 256c
// Renvoie s'il y a eu une erreur ou pas..
// Cette fonction utilise l'algorithme "median cut" (Optimize_palette) pour trouver la palette, et diffuse les erreurs avec floyd-steinberg.
int Convert_24b_bitmap_to_256(T_Bitmap256 dest,T_Bitmap24B source,int width,int height,T_Components * palette)
{
  T_Conversion_table * table; // table de conversion
  int                ip;    // index de précision pour la conversion
  // On essaye d'obtenir une table de conversion qui loge en mémoire, avec la
  // meilleure précision possible
  for (ip=0;ip<(10*3);ip+=3)
  {
    table=Optimize_palette(source,width*height,palette,precision_24b[ip+0],
                            precision_24b[ip+1],precision_24b[ip+2]);
    if (table!=0)
      break;
  }
  if (table!=0)
  {
    Convert_24b_bitmap_to_256_Floyd_Steinberg(dest,source,width,height,palette,table);
    CT_delete(table);
    return 0;
  }
  else
    return 1;
}