The cluster system for color reduction when loading a truecolor image now uses a linked list instead of a table. This fix a potential problem because there was an overlapping memcpy. It also avoids copying the big cluster table around and just moves pointer around. It should be faster but I did not really noticed any change on my computer :(. Maybe it's too fast...

The same can probably be done for the gradient sets.


git-svn-id: svn://pulkomandy.tk/GrafX2/trunk@927 416bcca6-2ee7-4201-b75f-2eb2f807beb1
This commit is contained in:
Adrien Destugues 2009-07-16 12:57:11 +00:00
parent 3a7edb8323
commit e877292ca2
4 changed files with 176 additions and 166 deletions

View File

@ -210,7 +210,7 @@ else
# Compiles a regular linux exectutable for the native platform # Compiles a regular linux exectutable for the native platform
BIN = grafx2 BIN = grafx2
COPT = -W -Wall -Wdeclaration-after-statement -O$(OPTIM) -std=c99 -c -g `sdl-config --cflags` $(TTFCOPT) COPT = -W -Wall -Wdeclaration-after-statement -std=c99 -c -g `sdl-config --cflags` $(TTFCOPT)
LOPT = `sdl-config --libs` -lSDL_image $(TTFLOPT) -lpng LOPT = `sdl-config --libs` -lSDL_image $(TTFLOPT) -lpng
# Use gcc for compiling. Use ncc to build a callgraph and analyze the code. # Use gcc for compiling. Use ncc to build a callgraph and analyze the code.
CC = gcc CC = gcc

327
op_c.c
View File

@ -657,37 +657,40 @@ void Cluster_compute_hue(T_Cluster * c,T_Occurrence_table * to)
//////////////////////////// Mthodes de gestion des ensembles de clusters // //////////////////////////// Mthodes 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) void CS_Init(T_Cluster_set * cs,T_Occurrence_table * to)
{ {
cs->clusters[0].Rmin=cs->clusters[0].rmin=0; cs->clusters->Rmin = cs->clusters->rmin = 0;
cs->clusters[0].Gmin=cs->clusters[0].vmin=0; cs->clusters->Gmin = cs->clusters->vmin = 0;
cs->clusters[0].Bmin=cs->clusters[0].bmin=0; cs->clusters->Bmin = cs->clusters->bmin = 0;
cs->clusters[0].Rmax=cs->clusters[0].rmax=to->rng_r-1; cs->clusters->Rmax = cs->clusters->rmax = to->rng_r-1;
cs->clusters[0].Vmax=cs->clusters[0].vmax=to->rng_g-1; cs->clusters->Vmax = cs->clusters->vmax = to->rng_g-1;
cs->clusters[0].Bmax=cs->clusters[0].bmax=to->rng_b-1; cs->clusters->Bmax = cs->clusters->bmax = to->rng_b-1;
Cluster_analyser(cs->clusters+0,to); cs->clusters->next = NULL;
// Et hop : le 1er ensemble de couleurs est initialis Cluster_analyser(cs->clusters,to);
cs->nb=1; cs->nb=1;
} }
/// Allocate a new cluster set
T_Cluster_set * CS_New(int nbmax,T_Occurrence_table * to) T_Cluster_set * CS_New(int nbmax,T_Occurrence_table * to)
{ {
T_Cluster_set * n; T_Cluster_set * n;
n=(T_Cluster_set *)malloc(sizeof(T_Cluster_set)); n=(T_Cluster_set *)malloc(sizeof(T_Cluster_set));
if (n!=0) if (n != NULL)
{ {
// On recopie les paramŠtres demands // On recopie les paramŠtres demands
n->nb_max=OT_count_colors(to); 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 à 256 (nombre de couleurs voulu au final) // 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) if (n->nb_max>nbmax)
{ {
n->nb_max=nbmax; n->nb_max=nbmax;
} }
// On tente d'allouer la table // On tente d'allouer le premier cluster
n->clusters=(T_Cluster *)malloc(nbmax*sizeof(T_Cluster)); n->clusters=(T_Cluster *)malloc(sizeof(T_Cluster));
if (n->clusters!=NULL) if (n->clusters!=NULL)
// C'est bon! On initialise // C'est bon! On initialise
CS_Init(n,to); CS_Init(n,to);
@ -702,68 +705,74 @@ T_Cluster_set * CS_New(int nbmax,T_Occurrence_table * to)
return n; return n;
} }
/// Free a cluster set
void CS_Delete(T_Cluster_set * cs) void CS_Delete(T_Cluster_set * cs)
{ {
free(cs->clusters); T_Cluster* nxt;
free(cs); 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) void CS_Get(T_Cluster_set * cs,T_Cluster * c)
{ {
int index; T_Cluster* current = cs->clusters;
T_Cluster* prev = NULL;
// On cherche un cluster que l'on peut couper en deux, donc avec au moins deux valeurs // Search a cluster with at least 2 distinct colors so we can split it
// différentes sur l'une des composantes do
for (index=0;index<cs->nb;index++) {
if ( (cs->clusters[index].rmin<cs->clusters[index].rmax) || if ( (current->rmin < current->rmax) ||
(cs->clusters[index].vmin<cs->clusters[index].vmax) || (current->vmin < current->vmax) ||
(cs->clusters[index].bmin<cs->clusters[index].bmax) ) (current->bmin < current->bmax) )
break; break;
// On le recopie dans c prev = current;
*c=cs->clusters[index];
// On décrémente le nombre et on décale tous les clusters suivants } while((current = current -> next));
// Sachant qu'on va réinsérer juste après, il me semble que ça serait une bonne idée de gérer les clusters
// comme une liste chainée... on n'a aucun accès direct dedans, que des parcours ... // copy it to c
*c = *current;
// remove it from the list
cs->nb--; cs->nb--;
memcpy((cs->clusters+index),(cs->clusters+index+1),(cs->nb-index)*sizeof(T_Cluster));
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) void CS_Set(T_Cluster_set * cs,T_Cluster * c)
{ {
int index; T_Cluster* current = cs->clusters;
// int decalage; T_Cluster* prev = NULL;
// Le tableau des clusters est trié par nombre d'occurences. Donc on cherche la position du premier cluster // Search the first cluster that is smaller than ours
// qui est plus grand que le notre if(current != NULL) // don't search if the list is empty
for (index=0;index<cs->nb;index++) do
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. if (current->occurences < c->occurences)
// On doit donc décaler les ensembles suivants vers la fin pour se faire break;
// une place dans la liste. prev = current;
} while((current = current -> next));
//for (decalage=cs->nb;decalage>index;decalage--) // Now insert our cluster just before the one we found
// memcpy((cs->clusters+decalage),(cs->clusters+decalage-1),sizeof(T_Cluster)); c -> next = current;
memmove(cs->clusters+index+1,cs->clusters+index,(cs->nb-index)*sizeof(T_Cluster));
}
cs->clusters[index]=*c; current = malloc(sizeof(T_Cluster));
cs->nb++; *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 : // Détermination de la meilleure palette en utilisant l'algo Median Cut :
@ -802,96 +811,91 @@ void CS_Generate(T_Cluster_set * cs,T_Occurrence_table * to)
void CS_Compute_colors(T_Cluster_set * cs,T_Occurrence_table * to) void CS_Compute_colors(T_Cluster_set * cs,T_Occurrence_table * to)
{ {
int index;
T_Cluster * c; T_Cluster * c;
for (index=0,c=cs->clusters;index<cs->nb;index++,c++) for (c=cs->clusters;c!=NULL;c=c->next)
Cluster_compute_hue(c,to); Cluster_compute_hue(c,to);
} }
void CS_Sort_by_chrominance(T_Cluster_set * cs) void CS_Sort_by_chrominance(T_Cluster_set * cs)
{ {
int byte_used[256]; T_Cluster* nc;
int decalages[256]; T_Cluster* prev = NULL;
int index; T_Cluster* place;
T_Cluster * nc; T_Cluster* newlist = NULL;
nc=(T_Cluster *)malloc(cs->nb_max*sizeof(T_Cluster)); while((nc = cs->clusters))
{
// Remove the first cluster from the original list
nc = cs->clusters;
cs->clusters = cs->clusters->next;
// Initialisation de la table d'occurence de chaque octet // Find his position in the new list
for (index=0;index<256;index++) for(place = newlist;place != NULL; place = place->next)
byte_used[index]=0; {
if(place->h > nc->h) break;
prev = place;
}
// Comptage du nombre d'occurences de chaque octet // Chain it there
for (index=0;index<cs->nb;index++) nc->next = place;
byte_used[cs->clusters[index].h]++; if(prev) prev->next = nc;
else newlist = nc;
// Calcul de la table des dcalages }
decalages[0]=0;
for (index=1;index<256;index++)
decalages[index]=decalages[index-1]+byte_used[index-1];
// Copie rordonne dans la nouvelle liste // Put the new list bavk in place
for (index=0;index<cs->nb;index++) cs->clusters = newlist;
{
nc[decalages[cs->clusters[index].h]]=cs->clusters[index];
decalages[cs->clusters[index].h]++;
}
// Remplacement de la liste dsordonne par celle trie
free(cs->clusters);
cs->clusters=nc;
} }
void CS_Sort_by_luminance(T_Cluster_set * cs) void CS_Sort_by_luminance(T_Cluster_set * cs)
{ {
int byte_used[256]; T_Cluster* nc;
int decalages[256]; T_Cluster* prev = NULL;
int index; T_Cluster* place;
T_Cluster * nc; T_Cluster* newlist = NULL;
nc=(T_Cluster *)malloc(cs->nb_max*sizeof(T_Cluster)); while((nc = cs->clusters))
{
// Remove the first cluster from the original list
nc = cs->clusters;
cs->clusters = cs->clusters->next;
// Initialisation de la table d'occurence de chaque octet // Find his position in the new list
for (index=0;index<256;index++) for(place = newlist;place != NULL; place = place->next)
byte_used[index]=0; {
if(place->l > nc->l) break;
prev = place;
}
// Comptage du nombre d'occurences de chaque octet // Chain it there
for (index=0;index<cs->nb;index++) nc->next = place;
byte_used[cs->clusters[index].l]++; if(prev) prev->next = nc;
else newlist = nc;
// Calcul de la table des dcalages }
decalages[0]=0;
for (index=1;index<256;index++)
decalages[index]=decalages[index-1]+byte_used[index-1];
// Copie rordonne dans la nouvelle liste // Put the new list bavk in place
for (index=0;index<cs->nb;index++) cs->clusters = newlist;
{
nc[decalages[cs->clusters[index].l]]=cs->clusters[index];
decalages[cs->clusters[index].l]++;
}
// Remplacement de la liste dsordonne par celle trie
free(cs->clusters);
cs->clusters=nc;
} }
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,T_Conversion_table * tc,T_Components * palette) void CS_Generate_color_table_and_palette(T_Cluster_set * cs,T_Conversion_table * tc,T_Components * palette)
{ {
int index; int index;
int r,g,b; int r,g,b;
T_Cluster* current = cs->clusters;
for (index=0;index<cs->nb;index++) for (index=0;index<cs->nb;index++)
{ {
palette[index].R=cs->clusters[index].r; palette[index].R=current->r;
palette[index].G=cs->clusters[index].g; palette[index].G=current->g;
palette[index].B=cs->clusters[index].b; palette[index].B=current->b;
for (r=cs->clusters[index].Rmin;r<=cs->clusters[index].Rmax;r++) for (r=current->Rmin; r<=current->Rmax; r++)
for (g=cs->clusters[index].Gmin;g<=cs->clusters[index].Vmax;g++) for (g=current->Gmin;g<=current->Vmax;g++)
for (b=cs->clusters[index].Bmin;b<=cs->clusters[index].Bmax;b++) for (b=current->Bmin;b<=current->Bmax;b++)
CT_set(tc,r,g,b,index); CT_set(tc,r,g,b,index);
current = current->next;
} }
} }
@ -902,9 +906,9 @@ void CS_Generate_color_table_and_palette(T_Cluster_set * cs,T_Conversion_table *
void GS_Init(T_Gradient_set * ds,T_Cluster_set * cs) void GS_Init(T_Gradient_set * ds,T_Cluster_set * cs)
{ {
ds->gradients[0].nb_colors=1; ds->gradients[0].nb_colors=1;
ds->gradients[0].min=cs->clusters[0].h; ds->gradients[0].min=cs->clusters->h;
ds->gradients[0].max=cs->clusters[0].h; ds->gradients[0].max=cs->clusters->h;
ds->gradients[0].hue=cs->clusters[0].h; ds->gradients[0].hue=cs->clusters->h;
// Et hop : le 1er ensemble de dgrads est initialis // Et hop : le 1er ensemble de dgrads est initialis
ds->nb=1; ds->nb=1;
} }
@ -943,57 +947,60 @@ void GS_Delete(T_Gradient_set * ds)
void GS_Generate(T_Gradient_set * ds,T_Cluster_set * cs) void GS_Generate(T_Gradient_set * ds,T_Cluster_set * cs)
{ {
int ic,id; // Les indexs de parcours des ensembles int id; // Les indexs de parcours des ensembles
int best_gradient; // Meilleur dgrad int best_gradient; // Meilleur dgrad
int best_diff; // Meilleure diffrence de chrominance int best_diff; // Meilleure diffrence de chrominance
int diff; // difference de chrominance courante int diff; // difference de chrominance courante
T_Cluster * current = cs->clusters;
// Pour chacun des clusters … traiter // Pour chacun des clusters … traiter
for (ic=0;ic<cs->nb;ic++) do
{ {
// On recherche le dgrad le plus proche de la chrominance du cluster // On recherche le dgrad le plus proche de la chrominance du cluster
best_gradient=-1; best_gradient=-1;
best_diff=99999999; best_diff=99999999;
for (id=0;id<ds->nb;id++) for (id=0;id<ds->nb;id++)
{ {
diff=abs(cs->clusters[ic].h - ds->gradients[id].hue); diff=abs(current->h - ds->gradients[id].hue);
if ((best_diff>diff) && (diff<16)) if ((best_diff>diff) && (diff<16))
{ {
best_gradient=id; best_gradient=id;
best_diff=diff; best_diff=diff;
} }
} }
// Si on a trouv un dgrad dans lequel inclure le cluster // Si on a trouv un dgrad dans lequel inclure le cluster
if (best_gradient!=-1) if (best_gradient!=-1)
{ {
// On met … jour le dgrad // On met … jour le dgrad
if (cs->clusters[ic].h < ds->gradients[best_gradient].min) if (current->h < ds->gradients[best_gradient].min)
ds->gradients[best_gradient].min=cs->clusters[ic].h; ds->gradients[best_gradient].min=current->h;
if (cs->clusters[ic].h > ds->gradients[best_gradient].max) if (current->h > ds->gradients[best_gradient].max)
ds->gradients[best_gradient].max=cs->clusters[ic].h; ds->gradients[best_gradient].max=current->h;
ds->gradients[best_gradient].hue=((ds->gradients[best_gradient].hue* ds->gradients[best_gradient].hue=((ds->gradients[best_gradient].hue*
ds->gradients[best_gradient].nb_colors) ds->gradients[best_gradient].nb_colors)
+cs->clusters[ic].h) +current->h)
/(ds->gradients[best_gradient].nb_colors+1); /(ds->gradients[best_gradient].nb_colors+1);
ds->gradients[best_gradient].nb_colors++; ds->gradients[best_gradient].nb_colors++;
} }
else else
{ {
// On cre un nouveau dgrad // On cre un nouveau dgrad
best_gradient=ds->nb; best_gradient=ds->nb;
ds->gradients[best_gradient].nb_colors=1; ds->gradients[best_gradient].nb_colors=1;
ds->gradients[best_gradient].min=cs->clusters[ic].h; ds->gradients[best_gradient].min=current->h;
ds->gradients[best_gradient].max=cs->clusters[ic].h; ds->gradients[best_gradient].max=current->h;
ds->gradients[best_gradient].hue=cs->clusters[ic].h; ds->gradients[best_gradient].hue=current->h;
ds->nb++; ds->nb++;
} }
cs->clusters[ic].h=best_gradient; current->h=best_gradient;
} } while((current = current->next));
// On redistribue les valeurs dans les clusters // On redistribue les valeurs dans les clusters
for (ic=0;ic<cs->nb;ic++) current = cs -> clusters;
cs->clusters[ic].h=ds->gradients[cs->clusters[ic].h].hue; do
current->h=ds->gradients[current->h].hue;
while((current = current ->next));
} }

4
op_c.h
View File

@ -88,7 +88,7 @@ typedef struct
///////////////////////////////////////// Définition d'un ensemble de couleur ///////////////////////////////////////// Définition d'un ensemble de couleur
typedef struct typedef struct S_Cluster
{ {
int occurences; // Nb total d'occurences des couleurs de l'ensemble int occurences; // Nb total d'occurences des couleurs de l'ensemble
@ -106,6 +106,8 @@ typedef struct
byte r,g,b; // color synthétisant l'ensemble byte r,g,b; // color synthétisant l'ensemble
byte h; // Chrominance byte h; // Chrominance
byte l; // Luminosité byte l; // Luminosité
struct S_Cluster* next;
} T_Cluster; } T_Cluster;

View File

@ -57,7 +57,8 @@ int Create_ConfigDirectory(char * config_dir)
// Determine which directory contains the executable. // Determine which directory contains the executable.
// IN: Main's argv[0], some platforms need it, some don't. // IN: Main's argv[0], some platforms need it, some don't.
// OUT: Write into program_dir. Trailing / or \ is kept. // OUT: Write into program_dir. Trailing / or \ is kept.
// Note : in fact this is only used to check for the datafiles and fonts in this same directory. // Note : in fact this is only used to check for the datafiles and fonts in
// this same directory.
void Set_program_directory(ARG_UNUSED const char * argv0,char * program_dir) void Set_program_directory(ARG_UNUSED const char * argv0,char * program_dir)
{ {
#undef ARG_UNUSED #undef ARG_UNUSED
@ -76,9 +77,9 @@ void Set_program_directory(ARG_UNUSED const char * argv0,char * program_dir)
// Others: The part of argv[0] before the executable name. // Others: The part of argv[0] before the executable name.
// Keep the last \ or /. // Keep the last \ or /.
// Note that on Unix, once installed, the executable is called from a shell script // Note that on Unix, once installed, the executable is called from a shell
// sitting in /usr/local/bin/, this allows argv[0] to contain the full path. // script sitting in /usr/local/bin/, this allows argv[0] to contain the full
// On Windows, Mingw32 already provides the full path in all cases. // path. On Windows, Mingw32 already provides the full path in all cases.
#else #else
Extract_path(program_dir, argv0); Extract_path(program_dir, argv0);
#endif #endif