1220 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1220 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*  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 <http://www.gnu.org/licenses/>
 | 
						||
*/
 | 
						||
#include <unistd.h>
 | 
						||
#include <stdlib.h>
 | 
						||
#include <string.h>
 | 
						||
#include <stdio.h>
 | 
						||
#include <fcntl.h>
 | 
						||
#include <sys/stat.h>
 | 
						||
#include <math.h>
 | 
						||
 | 
						||
#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<61>tres demand<6E>s
 | 
						||
    n->nbb_r=nbb_r;
 | 
						||
    n->nbb_g=nbb_g;
 | 
						||
    n->nbb_b=nbb_b;
 | 
						||
 | 
						||
    // On calcule les autres
 | 
						||
    n->rng_r=(1<<nbb_r);
 | 
						||
    n->rng_g=(1<<nbb_g);
 | 
						||
    n->rng_b=(1<<nbb_b);
 | 
						||
    n->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 <20> 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<<t->dec_r) | (g<<t->dec_g) | (b<<t->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<<t->dec_r) | (g<<t->dec_g) | (b<<t->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 <20> 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<61>tres demand<6E>s
 | 
						||
    n->nbb_r=nbb_r;
 | 
						||
    n->nbb_g=nbb_g;
 | 
						||
    n->nbb_b=nbb_b;
 | 
						||
 | 
						||
    // On calcule les autres
 | 
						||
    n->rng_r=(1<<nbb_r);
 | 
						||
    n->rng_g=(1<<nbb_g);
 | 
						||
    n->rng_b=(1<<nbb_b);
 | 
						||
    n->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 <20> 0
 | 
						||
      OT_init(n);
 | 
						||
    else
 | 
						||
    {
 | 
						||
      // Table impossible <20> 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<<t->dec_r) | (g<<t->dec_g) | (b<<t->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<<t->dec_r) | (g<<t->dec_g) | (b<<t->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 <20> tester
 | 
						||
  int i;   // Compteur de couleurs test<73>es
 | 
						||
 | 
						||
  val=0;
 | 
						||
  nb=(t->rng_r)*(t->rng_g)*(t->rng_b);
 | 
						||
  for (i=0;i<nb;i++)
 | 
						||
    if (t->table[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<70>d<EFBFBD>cale tout pour <20>viter de faire trop de bazar en se for<6F>ant <20> 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 (r<rmin) rmin=r;
 | 
						||
          else if (r>rmax) rmax=r;
 | 
						||
          if (g<vmin) vmin=g;
 | 
						||
          else if (g>vmax) vmax=g;
 | 
						||
          if (b<bmin) bmin=b;
 | 
						||
          else if (b>bmax) bmax=b;
 | 
						||
          c->occurences+=nbocc;
 | 
						||
        }
 | 
						||
      }
 | 
						||
  */
 | 
						||
 | 
						||
  // On recherche le minimum et le maximum en parcourant le cluster selon chaque composante, 
 | 
						||
  // <20>a <20>vite des acc<63>s m<>moires inutiles, de plus chaque boucle est plus petite que la 
 | 
						||
  // pr<70>c<EFBFBD>dente puisqu'on connait une borne suppl<70>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 G<B
 | 
						||
      c->plus_large=2;
 | 
						||
    }
 | 
						||
  }
 | 
						||
  else
 | 
						||
  {
 | 
						||
    // R>G
 | 
						||
    if (r>=b)
 | 
						||
    {
 | 
						||
      // R>G et R>=B
 | 
						||
      c->plus_large=0;
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
      // R>G et R<B
 | 
						||
      c->plus_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<<to->red_r)/c->occurences;
 | 
						||
  c->g=(cumul_g<<to->red_g)/c->occurences;
 | 
						||
  c->b=(cumul_b<<to->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 //
 | 
						||
/////////////////////////////////////////////////////////////////////////////
 | 
						||
 | 
						||
void CS_Init(T_Cluster_set * cs,T_Occurrence_table * to)
 | 
						||
{
 | 
						||
  cs->clusters[0].Rmin=cs->clusters[0].rmin=0;
 | 
						||
  cs->clusters[0].Gmin=cs->clusters[0].vmin=0;
 | 
						||
  cs->clusters[0].Bmin=cs->clusters[0].bmin=0;
 | 
						||
  cs->clusters[0].Rmax=cs->clusters[0].rmax=to->rng_r-1;
 | 
						||
  cs->clusters[0].Vmax=cs->clusters[0].vmax=to->rng_g-1;
 | 
						||
  cs->clusters[0].Bmax=cs->clusters[0].bmax=to->rng_b-1;
 | 
						||
  Cluster_analyser(cs->clusters+0,to);
 | 
						||
  // Et hop : le 1er ensemble de couleurs est initialis<69>
 | 
						||
  cs->nb=1;
 | 
						||
}
 | 
						||
 | 
						||
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!=0)
 | 
						||
  {
 | 
						||
    // On recopie les param<61>tres demand<6E>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 limit <20> 256 (nombre de couleurs voulu au final)
 | 
						||
    if (n->nb_max>nbmax)
 | 
						||
    {
 | 
						||
      n->nb_max=nbmax;
 | 
						||
    }
 | 
						||
 | 
						||
    // On tente d'allouer la table
 | 
						||
    n->clusters=(T_Cluster *)malloc(nbmax*sizeof(T_Cluster));
 | 
						||
    if (n->clusters!=NULL)
 | 
						||
      // C'est bon! On initialise
 | 
						||
      CS_Init(n,to);
 | 
						||
    else
 | 
						||
    {
 | 
						||
      // Table impossible <20> allouer
 | 
						||
      free(n);
 | 
						||
      n=0;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  return n;
 | 
						||
}
 | 
						||
 | 
						||
void CS_Delete(T_Cluster_set * cs)
 | 
						||
{
 | 
						||
  free(cs->clusters);
 | 
						||
  free(cs);
 | 
						||
}
 | 
						||
 | 
						||
void CS_Get(T_Cluster_set * cs,T_Cluster * c)
 | 
						||
{
 | 
						||
  int index;
 | 
						||
 | 
						||
  // On cherche un cluster que l'on peut couper en deux, donc avec au moins deux valeurs
 | 
						||
  // diff<66>rentes sur l'une des composantes
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
    if ( (cs->clusters[index].rmin<cs->clusters[index].rmax) ||
 | 
						||
         (cs->clusters[index].vmin<cs->clusters[index].vmax) ||
 | 
						||
         (cs->clusters[index].bmin<cs->clusters[index].bmax) )
 | 
						||
      break;
 | 
						||
 | 
						||
  // On le recopie dans c
 | 
						||
  *c=cs->clusters[index];
 | 
						||
 | 
						||
  // On d<>cr<63>mente le nombre et on d<>cale tous les clusters suivants
 | 
						||
  // Sachant qu'on va r<>ins<6E>rer juste apr<70>s, il me semble que <20>a serait une bonne id<69>e de g<>rer les clusters 
 | 
						||
  // comme une liste chain<69>e... on n'a aucun acc<63>s direct dedans, que des parcours ...
 | 
						||
  cs->nb--;
 | 
						||
  memcpy((cs->clusters+index),(cs->clusters+index+1),(cs->nb-index)*sizeof(T_Cluster));
 | 
						||
}
 | 
						||
 | 
						||
void CS_Set(T_Cluster_set * cs,T_Cluster * c)
 | 
						||
{
 | 
						||
  int index;
 | 
						||
  // int decalage;
 | 
						||
 | 
						||
  // Le tableau des clusters est tri<72> par nombre d'occurences. Donc on cherche la position du premier cluster 
 | 
						||
  // qui est plus grand que le notre
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
    if (cs->clusters[index].occurences<c->occurences)
 | 
						||
/*
 | 
						||
    if (((OPTPAL_Cluster[index].rmax-OPTPAL_Cluster[index].rmin+1)*
 | 
						||
         (OPTPAL_Cluster[index].gmax-OPTPAL_Cluster[index].gmin+1)*
 | 
						||
         (OPTPAL_Cluster[index].bmax-OPTPAL_Cluster[index].bmin+1))
 | 
						||
        <
 | 
						||
        ((Set->rmax-Set->rmin+1)*
 | 
						||
         (Set->gmax-Set->gmin+1)*
 | 
						||
         (Set->bmax-Set->bmin+1))
 | 
						||
       )
 | 
						||
*/
 | 
						||
      break;
 | 
						||
 | 
						||
  if (index<cs->nb)
 | 
						||
  {
 | 
						||
    // On distingue ici une insertion plutot qu'un placement en fin de liste.
 | 
						||
    // On doit donc d<>caler les ensembles suivants vers la fin pour se faire
 | 
						||
    // une place dans la liste.
 | 
						||
 | 
						||
    //for (decalage=cs->nb;decalage>index;decalage--)
 | 
						||
    //  memcpy((cs->clusters+decalage),(cs->clusters+decalage-1),sizeof(T_Cluster));
 | 
						||
    memmove(cs->clusters+index+1,cs->clusters+index,(cs->nb-index)*sizeof(T_Cluster));
 | 
						||
  }
 | 
						||
 | 
						||
  cs->clusters[index]=*c;
 | 
						||
  cs->nb++;
 | 
						||
}
 | 
						||
 | 
						||
// D<>termination de la meilleure palette en utilisant l'algo Median Cut :
 | 
						||
// 1) On consid<69>re l'espace (R,G,B) comme 1 bo<62>te
 | 
						||
// 2) On cherche les extr<74>mes de la bo<62>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<62>te en deux au milieu, et on compacte pour que chaque bord corresponde bien <20> un pixel extreme
 | 
						||
// 5) On recommence <20> couper selon le plus grand axe toutes bo<62>tes confondues
 | 
						||
// 6) On s'arr<72>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->nb<cs->nb_max)
 | 
						||
  {
 | 
						||
    // On r<>cup<75>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)
 | 
						||
{
 | 
						||
  int index;
 | 
						||
  T_Cluster * c;
 | 
						||
 | 
						||
  for (index=0,c=cs->clusters;index<cs->nb;index++,c++)
 | 
						||
    Cluster_compute_hue(c,to);
 | 
						||
}
 | 
						||
 | 
						||
void CS_Sort_by_chrominance(T_Cluster_set * cs)
 | 
						||
{
 | 
						||
  int byte_used[256];
 | 
						||
  int decalages[256];
 | 
						||
  int index;
 | 
						||
  T_Cluster * nc;
 | 
						||
 | 
						||
  nc=(T_Cluster *)malloc(cs->nb_max*sizeof(T_Cluster));
 | 
						||
 | 
						||
  // Initialisation de la table d'occurence de chaque octet
 | 
						||
  for (index=0;index<256;index++)
 | 
						||
    byte_used[index]=0;
 | 
						||
 | 
						||
  // Comptage du nombre d'occurences de chaque octet
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
    byte_used[cs->clusters[index].h]++;
 | 
						||
 | 
						||
  // Calcul de la table des d<>calages
 | 
						||
  decalages[0]=0;
 | 
						||
  for (index=1;index<256;index++)
 | 
						||
    decalages[index]=decalages[index-1]+byte_used[index-1];
 | 
						||
 | 
						||
  // Copie r<>ordonn<6E>e dans la nouvelle liste
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
  {
 | 
						||
    nc[decalages[cs->clusters[index].h]]=cs->clusters[index];
 | 
						||
    decalages[cs->clusters[index].h]++;
 | 
						||
  }
 | 
						||
 | 
						||
  // Remplacement de la liste d<>sordonn<6E>e par celle tri<72>e
 | 
						||
  free(cs->clusters);
 | 
						||
  cs->clusters=nc;
 | 
						||
}
 | 
						||
 | 
						||
void CS_Sort_by_luminance(T_Cluster_set * cs)
 | 
						||
{
 | 
						||
  int byte_used[256];
 | 
						||
  int decalages[256];
 | 
						||
  int index;
 | 
						||
  T_Cluster * nc;
 | 
						||
 | 
						||
  nc=(T_Cluster *)malloc(cs->nb_max*sizeof(T_Cluster));
 | 
						||
 | 
						||
  // Initialisation de la table d'occurence de chaque octet
 | 
						||
  for (index=0;index<256;index++)
 | 
						||
    byte_used[index]=0;
 | 
						||
 | 
						||
  // Comptage du nombre d'occurences de chaque octet
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
    byte_used[cs->clusters[index].l]++;
 | 
						||
 | 
						||
  // Calcul de la table des d<>calages
 | 
						||
  decalages[0]=0;
 | 
						||
  for (index=1;index<256;index++)
 | 
						||
    decalages[index]=decalages[index-1]+byte_used[index-1];
 | 
						||
 | 
						||
  // Copie r<>ordonn<6E>e dans la nouvelle liste
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
  {
 | 
						||
    nc[decalages[cs->clusters[index].l]]=cs->clusters[index];
 | 
						||
    decalages[cs->clusters[index].l]++;
 | 
						||
  }
 | 
						||
 | 
						||
  // Remplacement de la liste d<>sordonn<6E>e par celle tri<72>e
 | 
						||
  free(cs->clusters);
 | 
						||
  cs->clusters=nc;
 | 
						||
}
 | 
						||
 | 
						||
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,T_Conversion_table * tc,T_Components * palette)
 | 
						||
{
 | 
						||
  int index;
 | 
						||
  int r,g,b;
 | 
						||
 | 
						||
  for (index=0;index<cs->nb;index++)
 | 
						||
  {
 | 
						||
    palette[index].R=cs->clusters[index].r;
 | 
						||
    palette[index].G=cs->clusters[index].g;
 | 
						||
    palette[index].B=cs->clusters[index].b;
 | 
						||
 | 
						||
    for (r=cs->clusters[index].Rmin;r<=cs->clusters[index].Rmax;r++)
 | 
						||
      for (g=cs->clusters[index].Gmin;g<=cs->clusters[index].Vmax;g++)
 | 
						||
        for (b=cs->clusters[index].Bmin;b<=cs->clusters[index].Bmax;b++)
 | 
						||
          CT_set(tc,r,g,b,index);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/////////////////////////////////////////////////////////////////////////////
 | 
						||
///////////////////////////////////////// M<>thodes de gestion des d<>grad<61>s //
 | 
						||
/////////////////////////////////////////////////////////////////////////////
 | 
						||
 | 
						||
void GS_Init(T_Gradient_set * ds,T_Cluster_set * cs)
 | 
						||
{
 | 
						||
    ds->gradients[0].nb_colors=1;
 | 
						||
    ds->gradients[0].min=cs->clusters[0].h;
 | 
						||
    ds->gradients[0].max=cs->clusters[0].h;
 | 
						||
    ds->gradients[0].hue=cs->clusters[0].h;
 | 
						||
    // Et hop : le 1er ensemble de d<>grad<61>s est initialis<69>
 | 
						||
    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<61>tres demand<6E>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 <20> 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 ic,id; // Les indexs de parcours des ensembles
 | 
						||
    int best_gradient; // Meilleur d<>grad<61>
 | 
						||
    int best_diff; // Meilleure diff<66>rence de chrominance
 | 
						||
    int diff;  // difference de chrominance courante
 | 
						||
 | 
						||
    // Pour chacun des clusters <20> traiter
 | 
						||
    for (ic=0;ic<cs->nb;ic++)
 | 
						||
    {
 | 
						||
        // On recherche le d<>grad<61> le plus proche de la chrominance du cluster
 | 
						||
        best_gradient=-1;
 | 
						||
        best_diff=99999999;
 | 
						||
        for (id=0;id<ds->nb;id++)
 | 
						||
        {
 | 
						||
            diff=abs(cs->clusters[ic].h - ds->gradients[id].hue);
 | 
						||
            if ((best_diff>diff) && (diff<16))
 | 
						||
            {
 | 
						||
                best_gradient=id;
 | 
						||
                best_diff=diff;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        // Si on a trouv<75> un d<>grad<61> dans lequel inclure le cluster
 | 
						||
        if (best_gradient!=-1)
 | 
						||
    {
 | 
						||
      // On met <20> jour le d<>grad<61>
 | 
						||
      if (cs->clusters[ic].h < ds->gradients[best_gradient].min)
 | 
						||
        ds->gradients[best_gradient].min=cs->clusters[ic].h;
 | 
						||
      if (cs->clusters[ic].h > ds->gradients[best_gradient].max)
 | 
						||
        ds->gradients[best_gradient].max=cs->clusters[ic].h;
 | 
						||
      ds->gradients[best_gradient].hue=((ds->gradients[best_gradient].hue*
 | 
						||
                                ds->gradients[best_gradient].nb_colors)
 | 
						||
                               +cs->clusters[ic].h)
 | 
						||
                              /(ds->gradients[best_gradient].nb_colors+1);
 | 
						||
      ds->gradients[best_gradient].nb_colors++;
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
      // On cr<63>e un nouveau d<>grad<61>
 | 
						||
      best_gradient=ds->nb;
 | 
						||
      ds->gradients[best_gradient].nb_colors=1;
 | 
						||
      ds->gradients[best_gradient].min=cs->clusters[ic].h;
 | 
						||
      ds->gradients[best_gradient].max=cs->clusters[ic].h;
 | 
						||
      ds->gradients[best_gradient].hue=cs->clusters[ic].h;
 | 
						||
      ds->nb++;
 | 
						||
    }
 | 
						||
    cs->clusters[ic].h=best_gradient;
 | 
						||
  }
 | 
						||
 | 
						||
  // On redistribue les valeurs dans les clusters
 | 
						||
  for (ic=0;ic<cs->nb;ic++)
 | 
						||
    cs->clusters[ic].h=ds->gradients[cs->clusters[ic].h].hue;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
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<43>ation des <20>l<EFBFBD>ments n<>cessaires au calcul de palette optimis<69>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<6D>re <20>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<EFBFBD>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<69> pour chaque couleur
 | 
						||
        CS_Sort_by_luminance(cs);
 | 
						||
        CS_Sort_by_chrominance(cs);
 | 
						||
 | 
						||
        // Enfin on g<>n<EFBFBD>re la palette et la table de correspondance entre chaque couleur 24b et sa couleur palette associ<63>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<70>cision plus faible (3 derniers param<61>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 <20> mesure le bitmap source, donc soit on ne
 | 
						||
// s'en ressert pas, soit on passe <20> 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 <20> droite
 | 
						||
  u_minus1=next-1;   // Le pixel en bas <20> gauche
 | 
						||
  u_plus1 =next+1;   // Le pixel en bas <20> droite
 | 
						||
  d       =dest;
 | 
						||
 | 
						||
  // On parcours chaque pixel:
 | 
						||
  for (y_pos=0;y_pos<height;y_pos++)
 | 
						||
  {
 | 
						||
    for (x_pos=0;x_pos<width;x_pos++)
 | 
						||
    {
 | 
						||
      // On prends la meilleure couleur de la palette qui traduit la couleur
 | 
						||
      // 24 bits de la source:
 | 
						||
      red=current->R;
 | 
						||
      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 <20> 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+1<width)
 | 
						||
        {
 | 
						||
          // Modified_value fait la somme des 2 params en bornant sur [0,255]
 | 
						||
          c_plus1->R=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 <20> gauche:
 | 
						||
      if (y_pos+1<height)
 | 
						||
      {
 | 
						||
        e_red=(red*3)/16.0;
 | 
						||
        e_green =(green *3)/16.0;
 | 
						||
        e_blue =(blue *3)/16.0;
 | 
						||
        if (x_pos>0)
 | 
						||
        {
 | 
						||
          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 <20> droite:
 | 
						||
        if (x_pos+1<width)
 | 
						||
        {
 | 
						||
        e_red=(red/16.0);
 | 
						||
        e_green =(green /16.0);
 | 
						||
        e_blue =(blue /16.0);
 | 
						||
          u_plus1->R=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<70>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<70>cision pour la conversion
 | 
						||
 | 
						||
  // On essaye d'obtenir une table de conversion qui loge en m<>moire, avec la
 | 
						||
  // meilleure pr<70>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;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 |