* Remove debug output
* Further optimization to the colortree, now allocated in one single chunk of 4090 bytes (2051 times better than original code!). Not doing all the malloc/frees should be slightly faster, and it also helps with cache locality (the whole tree can fit in the cache). * Remove the useless color "balancing" multipliers that did more harm than good. We still need some improvements on the median cut, or maybe switch to a smarter algorithm; or add some cheats. See the JFIF quirks for an example. git-svn-id: svn://pulkomandy.tk/GrafX2/trunk@1876 416bcca6-2ee7-4201-b75f-2eb2f807beb1
This commit is contained in:
parent
58e8194f40
commit
4813314638
@ -34,23 +34,24 @@ but :
|
|||||||
* No loss of precision
|
* No loss of precision
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CT_Node* CT_new() {return NULL;}
|
CT_Tree* CT_new() {return calloc(1, sizeof(CT_Tree));}
|
||||||
|
|
||||||
// debug helper
|
// debug helper
|
||||||
|
/*
|
||||||
void CT_Print(CT_Node* node)
|
void CT_Print(CT_Node* node)
|
||||||
{
|
{
|
||||||
printf("R %d %d\tG %d %d\tB %d %d\ti %d\n",
|
printf("R %d %d\tG %d %d\tB %d %d\ti %d\n",
|
||||||
node->Rmin, node->Rmax, node->Gmin, node->Gmax,
|
node->Rmin, node->Rmax, node->Gmin, node->Gmax,
|
||||||
node->Bmin, node->Bmax, node->index);
|
node->Bmin, node->Bmax, node->index);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void CT_set(CT_Node** colorTree, byte Rmin, byte Gmin, byte Bmin,
|
void CT_set(CT_Tree* colorTree, byte Rmin, byte Gmin, byte Bmin,
|
||||||
byte Rmax, byte Gmax, byte Bmax, byte index)
|
byte Rmax, byte Gmax, byte Bmax, byte index)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
CT_Node* parent;
|
CT_Node* parent;
|
||||||
// Create and setup node
|
// Create and setup node
|
||||||
CT_Node* node = malloc(sizeof(CT_Node));
|
CT_Node* node = &colorTree->nodes[++colorTree->nodecount];
|
||||||
|
|
||||||
node->Rmin = Rmin;
|
node->Rmin = Rmin;
|
||||||
node->Gmin = Gmin;
|
node->Gmin = Gmin;
|
||||||
@ -58,20 +59,11 @@ void CT_set(CT_Node** colorTree, byte Rmin, byte Gmin, byte Bmin,
|
|||||||
node->Rmax = Rmax;
|
node->Rmax = Rmax;
|
||||||
node->Gmax = Gmax;
|
node->Gmax = Gmax;
|
||||||
node->Bmax = Bmax;
|
node->Bmax = Bmax;
|
||||||
node->index = index;
|
node->children[1] = index;
|
||||||
|
|
||||||
printf("Add node:");
|
|
||||||
CT_Print(node);
|
|
||||||
|
|
||||||
for(i = 0; i < 2; i++)
|
|
||||||
node->children[i] = NULL;
|
|
||||||
|
|
||||||
// Now insert it in tree
|
// Now insert it in tree
|
||||||
parent = *colorTree;
|
parent = &colorTree->nodes[0];
|
||||||
if (parent == NULL) {
|
if (parent != NULL) for(;;) {
|
||||||
// This is our first node.
|
|
||||||
*colorTree = node;
|
|
||||||
} else for(;;) {
|
|
||||||
// Find where to insert ourselves
|
// Find where to insert ourselves
|
||||||
|
|
||||||
// pre-condition: the parent we're looking at is a superset of the node we're inserting
|
// pre-condition: the parent we're looking at is a superset of the node we're inserting
|
||||||
@ -81,12 +73,12 @@ void CT_set(CT_Node** colorTree, byte Rmin, byte Gmin, byte Bmin,
|
|||||||
// 1 child: either we're included in the child, and recurse, or we''re not, and insert at child 1
|
// 1 child: either we're included in the child, and recurse, or we''re not, and insert at child 1
|
||||||
// 2 child: one of them has to be a superset of the node.
|
// 2 child: one of them has to be a superset of the node.
|
||||||
|
|
||||||
if (parent->children[0] == NULL)
|
if (parent->children[0] == 0)
|
||||||
{
|
{
|
||||||
parent->children[0] = node;
|
parent->children[0] = colorTree->nodecount;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
CT_Node* child0 = parent->children[0];
|
CT_Node* child0 = &colorTree->nodes[parent->children[0]];
|
||||||
if (child0->Rmin <= node->Rmin
|
if (child0->Rmin <= node->Rmin
|
||||||
&& child0->Gmin <= node->Gmin
|
&& child0->Gmin <= node->Gmin
|
||||||
&& child0->Bmin <= node->Bmin
|
&& child0->Bmin <= node->Bmin
|
||||||
@ -95,28 +87,31 @@ void CT_set(CT_Node** colorTree, byte Rmin, byte Gmin, byte Bmin,
|
|||||||
&& child0->Bmax >= node->Bmax
|
&& child0->Bmax >= node->Bmax
|
||||||
) {
|
) {
|
||||||
parent = child0;
|
parent = child0;
|
||||||
} else if(parent->children[1] == NULL)
|
} else if(parent->children[1] == 0)
|
||||||
{
|
{
|
||||||
parent->children[1] = node;
|
parent->children[1] = colorTree->nodecount;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
parent = parent->children[1];
|
parent = &colorTree->nodes[parent->children[1]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte CT_get(CT_Node* node, byte r, byte g, byte b)
|
byte CT_get(CT_Tree* tree, byte r, byte g, byte b)
|
||||||
{
|
{
|
||||||
// pre condition: node contains (rgb)
|
// pre condition: node contains (rgb)
|
||||||
// find the leaf that also contains (rgb)
|
// find the leaf that also contains (rgb)
|
||||||
|
|
||||||
|
CT_Node* node = &tree->nodes[0];
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if(node->children[0] == NULL)
|
if(node->children[0] == 0)
|
||||||
return node->index;
|
// return the palette index
|
||||||
|
return node->children[1];
|
||||||
else {
|
else {
|
||||||
// Left or right ?
|
// Left or right ?
|
||||||
CT_Node* child0 = node->children[0];
|
CT_Node* child0 = &tree->nodes[node->children[0]];
|
||||||
if (child0->Rmin <= r
|
if (child0->Rmin <= r
|
||||||
&& child0->Gmin <= g
|
&& child0->Gmin <= g
|
||||||
&& child0->Bmin <= b
|
&& child0->Bmin <= b
|
||||||
@ -128,20 +123,13 @@ byte CT_get(CT_Node* node, byte r, byte g, byte b)
|
|||||||
node = child0;
|
node = child0;
|
||||||
} else {
|
} else {
|
||||||
// right
|
// right
|
||||||
node = node->children[1];
|
node = &tree->nodes[node->children[1]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CT_delete(CT_Node* tree)
|
void CT_delete(CT_Tree* tree)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
if (tree == NULL)
|
|
||||||
return;
|
|
||||||
for (i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
CT_delete(tree->children[i]);
|
|
||||||
}
|
|
||||||
free(tree);
|
free(tree);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,17 +55,22 @@ typedef struct CT_Node_s
|
|||||||
// * makes them smaller
|
// * makes them smaller
|
||||||
// * helps with cache locality
|
// * helps with cache locality
|
||||||
|
|
||||||
// palette index (valid iff any child is NULL)
|
// Child nodes :
|
||||||
byte index;
|
// Either two indices in the colorTree array, or
|
||||||
|
// 0 and a palette index
|
||||||
// child nodes
|
// 0 is not a valid array index, because no node points to the root !
|
||||||
struct CT_Node_s* children[2];
|
word children[2];
|
||||||
} CT_Node;
|
} CT_Node;
|
||||||
|
|
||||||
CT_Node* CT_new();
|
typedef struct ColorTree_S {
|
||||||
void CT_delete(CT_Node* t);
|
short nodecount;
|
||||||
byte CT_get(CT_Node* t,byte r,byte g,byte b);
|
CT_Node nodes[511];
|
||||||
void CT_set(CT_Node** colorTree, byte Rmin, byte Gmin, byte Bmin,
|
} CT_Tree;
|
||||||
|
|
||||||
|
CT_Tree* CT_new();
|
||||||
|
void CT_delete(CT_Tree* t);
|
||||||
|
byte CT_get(CT_Tree* t,byte r,byte g,byte b);
|
||||||
|
void CT_set(CT_Tree* colorTree, byte Rmin, byte Gmin, byte Bmin,
|
||||||
byte Rmax, byte Gmax, byte Bmax, byte index);
|
byte Rmax, byte Gmax, byte Bmax, byte index);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
30
src/op_c.c
30
src/op_c.c
@ -435,11 +435,9 @@ ENDCRUSH:
|
|||||||
c->bmin=bmin; c->bmax=bmax;
|
c->bmin=bmin; c->bmax=bmax;
|
||||||
|
|
||||||
// Find the longest axis to know which way to split the cluster
|
// Find the longest axis to know which way to split the cluster
|
||||||
// This multiplications are supposed to improve the result, but may or may not
|
r=(c->rmax-c->rmin);
|
||||||
// work, actually.
|
g=(c->vmax-c->vmin);
|
||||||
r=(c->rmax-c->rmin)*299;
|
b=(c->bmax-c->bmin);
|
||||||
g=(c->vmax-c->vmin)*587;
|
|
||||||
b=(c->bmax-c->bmin)*114;
|
|
||||||
|
|
||||||
if (g>=r)
|
if (g>=r)
|
||||||
{
|
{
|
||||||
@ -796,7 +794,7 @@ void CS_Set(T_Cluster_set * cs,T_Cluster * c)
|
|||||||
// 5) We take the box with the biggest number of pixels inside and we split it again
|
// 5) We take the box with the biggest number of pixels inside and we split it again
|
||||||
// 6) Iterate until there are 256 boxes. Associate each of them to its middle color
|
// 6) Iterate until there are 256 boxes. Associate each of them to its middle color
|
||||||
// At the same time, put the split clusters in the color tree for later palette lookup
|
// At the same time, put the split clusters in the color tree for later palette lookup
|
||||||
void CS_Generate(T_Cluster_set * cs, T_Occurrence_table * to, CT_Node** colorTree)
|
void CS_Generate(T_Cluster_set * cs, T_Occurrence_table * to, CT_Tree* colorTree)
|
||||||
{
|
{
|
||||||
T_Cluster current;
|
T_Cluster current;
|
||||||
T_Cluster Nouveau1;
|
T_Cluster Nouveau1;
|
||||||
@ -911,7 +909,7 @@ void CS_Sort_by_luminance(T_Cluster_set * cs)
|
|||||||
|
|
||||||
|
|
||||||
/// Generates the palette from the clusters, then the conversion table to map (RGB) to a palette index
|
/// Generates the palette from the clusters, then the conversion table to map (RGB) to a palette index
|
||||||
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,CT_Node** tc,T_Components * palette)
|
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,CT_Tree* tc,T_Components * palette)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
T_Cluster* current = cs->clusters;
|
T_Cluster* current = cs->clusters;
|
||||||
@ -1034,11 +1032,11 @@ void GS_Generate(T_Gradient_set * ds,T_Cluster_set * cs)
|
|||||||
|
|
||||||
|
|
||||||
/// Compute best palette for given picture.
|
/// Compute best palette for given picture.
|
||||||
CT_Node* Optimize_palette(T_Bitmap24B image, int size,
|
CT_Tree* Optimize_palette(T_Bitmap24B image, int size,
|
||||||
T_Components * palette, int r, int g, int b)
|
T_Components * palette, int r, int g, int b)
|
||||||
{
|
{
|
||||||
T_Occurrence_table * to;
|
T_Occurrence_table * to;
|
||||||
CT_Node* tc;
|
CT_Tree* tc;
|
||||||
T_Cluster_set * cs;
|
T_Cluster_set * cs;
|
||||||
T_Gradient_set * ds;
|
T_Gradient_set * ds;
|
||||||
|
|
||||||
@ -1050,13 +1048,13 @@ CT_Node* Optimize_palette(T_Bitmap24B image, int size,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tc = CT_new();
|
tc = CT_new();
|
||||||
/*
|
|
||||||
if (tc == NULL)
|
if (tc == NULL)
|
||||||
{
|
{
|
||||||
OT_delete(to);
|
OT_delete(to);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
// Count pixels for each color
|
// Count pixels for each color
|
||||||
OT_count_occurrences(to, image, size);
|
OT_count_occurrences(to, image, size);
|
||||||
|
|
||||||
@ -1071,7 +1069,7 @@ CT_Node* Optimize_palette(T_Bitmap24B image, int size,
|
|||||||
// Ok, everything was allocated
|
// Ok, everything was allocated
|
||||||
|
|
||||||
// Generate the cluster set with median cut algorithm
|
// Generate the cluster set with median cut algorithm
|
||||||
CS_Generate(cs, to, &tc);
|
CS_Generate(cs, to, tc);
|
||||||
//CS_Check(cs);
|
//CS_Check(cs);
|
||||||
|
|
||||||
// Compute the color data for each cluster (palette entry + HL)
|
// Compute the color data for each cluster (palette entry + HL)
|
||||||
@ -1091,7 +1089,7 @@ CT_Node* Optimize_palette(T_Bitmap24B image, int size,
|
|||||||
//CS_Check(cs);
|
//CS_Check(cs);
|
||||||
|
|
||||||
// And finally generate the conversion table to map RGB > pal. index
|
// And finally generate the conversion table to map RGB > pal. index
|
||||||
CS_Generate_color_table_and_palette(cs, &tc, palette);
|
CS_Generate_color_table_and_palette(cs, tc, palette);
|
||||||
//CS_Check(cs);
|
//CS_Check(cs);
|
||||||
|
|
||||||
CS_Delete(cs);
|
CS_Delete(cs);
|
||||||
@ -1119,7 +1117,7 @@ int Modified_value(int value,int modif)
|
|||||||
/// Convert a 24b image to 256 colors (with a given palette and conversion table)
|
/// Convert a 24b image to 256 colors (with a given palette and conversion table)
|
||||||
/// This destroys the 24b picture !
|
/// This destroys the 24b picture !
|
||||||
/// Uses floyd steinberg dithering.
|
/// Uses floyd steinberg dithering.
|
||||||
void Convert_24b_bitmap_to_256_Floyd_Steinberg(T_Bitmap256 dest,T_Bitmap24B source,int width,int height,T_Components * palette,CT_Node* tc)
|
void Convert_24b_bitmap_to_256_Floyd_Steinberg(T_Bitmap256 dest,T_Bitmap24B source,int width,int height,T_Components * palette,CT_Tree* tc)
|
||||||
{
|
{
|
||||||
T_Bitmap24B current;
|
T_Bitmap24B current;
|
||||||
T_Bitmap24B c_plus1;
|
T_Bitmap24B c_plus1;
|
||||||
@ -1215,7 +1213,7 @@ void Convert_24b_bitmap_to_256_Floyd_Steinberg(T_Bitmap256 dest,T_Bitmap24B sour
|
|||||||
/// Converts from 24b to 256c without dithering, using given conversion table
|
/// Converts from 24b to 256c without dithering, using given conversion table
|
||||||
void Convert_24b_bitmap_to_256_nearest_neighbor(T_Bitmap256 dest,
|
void Convert_24b_bitmap_to_256_nearest_neighbor(T_Bitmap256 dest,
|
||||||
T_Bitmap24B source, int width, int height, __attribute__((unused)) T_Components * palette,
|
T_Bitmap24B source, int width, int height, __attribute__((unused)) T_Components * palette,
|
||||||
CT_Node* tc)
|
CT_Tree* tc)
|
||||||
{
|
{
|
||||||
T_Bitmap24B current;
|
T_Bitmap24B current;
|
||||||
T_Bitmap256 d;
|
T_Bitmap256 d;
|
||||||
@ -1274,7 +1272,7 @@ int Convert_24b_bitmap_to_256(T_Bitmap256 dest,T_Bitmap24B source,int width,int
|
|||||||
return Convert_24b_bitmap_to_256_fast(dest, source, width, height, palette);
|
return Convert_24b_bitmap_to_256_fast(dest, source, width, height, palette);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
CT_Node* table; // table de conversion
|
CT_Tree* table; // table de conversion
|
||||||
int ip; // index de précision pour la 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
|
// On essaye d'obtenir une table de conversion qui loge en mémoire, avec la
|
||||||
|
|||||||
@ -158,9 +158,9 @@ T_Cluster_set * CS_New(int nbmax,T_Occurrence_table * to);
|
|||||||
void CS_Delete(T_Cluster_set * cs);
|
void CS_Delete(T_Cluster_set * cs);
|
||||||
void CS_Get(T_Cluster_set * cs,T_Cluster * c);
|
void CS_Get(T_Cluster_set * cs,T_Cluster * c);
|
||||||
void CS_Set(T_Cluster_set * cs,T_Cluster * c);
|
void CS_Set(T_Cluster_set * cs,T_Cluster * c);
|
||||||
void CS_Generate(T_Cluster_set * cs,T_Occurrence_table * to, CT_Node** colorTree);
|
void CS_Generate(T_Cluster_set * cs,T_Occurrence_table * to, CT_Tree* colorTree);
|
||||||
void CS_Compute_colors(T_Cluster_set * cs,T_Occurrence_table * to);
|
void CS_Compute_colors(T_Cluster_set * cs,T_Occurrence_table * to);
|
||||||
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,CT_Node** tc,T_Components * palette);
|
void CS_Generate_color_table_and_palette(T_Cluster_set * cs,CT_Tree* tc,T_Components * palette);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
//////////////////////////// Méthodes de gestion des ensembles de dégradés //
|
//////////////////////////// Méthodes de gestion des ensembles de dégradés //
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user