add oversampler
This commit is contained in:
469
oversampling/WDL/lice/lice_palette.cpp
Normal file
469
oversampling/WDL/lice/lice_palette.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
#include "lice.h"
|
||||
#include "../ptrlist.h"
|
||||
#include "../wdltypes.h"
|
||||
|
||||
|
||||
#define OCTREE_DEPTH 5 // every depth level adds 3 bits of RGB colorspace (depth 8 => 24-bit RGB)
|
||||
|
||||
struct ONode
|
||||
{
|
||||
WDL_INT64 colorcount; // number of color instances at or below this node
|
||||
WDL_INT64 sumrgb[3];
|
||||
int childflag; // 0=leaf, >0=index of single child, <0=branch
|
||||
int leafidx; // populated at the end
|
||||
ONode* next; // for OTree::branches
|
||||
ONode* children[8];
|
||||
};
|
||||
|
||||
struct OTree
|
||||
{
|
||||
int maxcolors;
|
||||
int leafcount;
|
||||
ONode* trunk;
|
||||
ONode* branches[OCTREE_DEPTH]; // linked lists of branches for each level of the tree
|
||||
ONode* spares;
|
||||
LICE_pixel* palette; // populated at the end
|
||||
bool palette_valid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int LICE_BuildPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int maxcolors)
|
||||
{
|
||||
void* tree = LICE_CreateOctree(maxcolors);
|
||||
LICE_BuildOctree(tree, bmp);
|
||||
int sz = LICE_ExtractOctreePalette(tree, palette);
|
||||
LICE_DestroyOctree(tree);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
static void AddColorToTree(OTree*, const LICE_pixel_chan *rgb);
|
||||
static int FindColorInTree(OTree*, const LICE_pixel_chan *rgb);
|
||||
static int PruneTree(OTree*);
|
||||
static void DeleteNode(OTree*, ONode*, ONode **delete_to);
|
||||
static int CollectLeaves(OTree*);
|
||||
static int CollectNodeLeaves(ONode* node, LICE_pixel* palette, int colorcount);
|
||||
|
||||
|
||||
void* LICE_CreateOctree(int maxcolors)
|
||||
{
|
||||
OTree* tree = new OTree;
|
||||
memset(tree, 0, sizeof(OTree));
|
||||
tree->maxcolors = maxcolors;
|
||||
tree->trunk = new ONode;
|
||||
memset(tree->trunk, 0, sizeof(ONode));
|
||||
tree->spares = NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
void LICE_ResetOctree(void *octree, int maxc)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree) return;
|
||||
|
||||
if (maxc > tree->maxcolors)
|
||||
{
|
||||
free(tree->palette);
|
||||
tree->palette=0;
|
||||
}
|
||||
|
||||
DeleteNode(tree, tree->trunk, &tree->spares);
|
||||
tree->leafcount = 0;
|
||||
tree->maxcolors = maxc;
|
||||
tree->palette_valid=false;
|
||||
memset(tree->branches,0,sizeof(tree->branches));
|
||||
|
||||
tree->trunk=tree->spares;
|
||||
if (tree->trunk) tree->spares = tree->trunk->next;
|
||||
else tree->trunk = new ONode;
|
||||
|
||||
memset(tree->trunk, 0, sizeof(ONode));
|
||||
}
|
||||
|
||||
void LICE_DestroyOctree(void* octree)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree) return;
|
||||
|
||||
DeleteNode(tree, tree->trunk, NULL);
|
||||
|
||||
ONode *p = tree->spares;
|
||||
while (p)
|
||||
{
|
||||
ONode *del = p;
|
||||
p = p->next;
|
||||
|
||||
delete del;
|
||||
}
|
||||
free(tree->palette);
|
||||
delete tree;
|
||||
}
|
||||
|
||||
|
||||
int LICE_BuildOctree(void* octree, LICE_IBitmap* bmp)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree || !bmp) return 0;
|
||||
|
||||
tree->palette_valid = false;
|
||||
|
||||
int y;
|
||||
const int h=bmp->getHeight();
|
||||
const int w=bmp->getWidth();
|
||||
const int rowspan = bmp->getRowSpan();
|
||||
const LICE_pixel *bits = bmp->getBits();
|
||||
for (y = 0; y < h; ++y)
|
||||
{
|
||||
const LICE_pixel *px = bits+y*rowspan;
|
||||
int x=w;
|
||||
while (x--)
|
||||
{
|
||||
AddColorToTree(tree, (const LICE_pixel_chan*)px);
|
||||
if (tree->leafcount > tree->maxcolors) PruneTree(tree);
|
||||
px++;
|
||||
}
|
||||
}
|
||||
|
||||
return tree->leafcount;
|
||||
}
|
||||
|
||||
int LICE_BuildOctreeForAlpha(void* octree, LICE_IBitmap* bmp, unsigned int minalpha)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree || !bmp) return 0;
|
||||
|
||||
tree->palette_valid = false;
|
||||
|
||||
int y;
|
||||
const int h=bmp->getHeight();
|
||||
const int w=bmp->getWidth();
|
||||
const int rowspan = bmp->getRowSpan();
|
||||
const LICE_pixel *bits = bmp->getBits();
|
||||
int pxcnt=0;
|
||||
for (y = 0; y < h; ++y)
|
||||
{
|
||||
const LICE_pixel *px = bits+y*rowspan;
|
||||
int x=w;
|
||||
while (x--)
|
||||
{
|
||||
if (px[LICE_PIXEL_A] >= minalpha)
|
||||
{
|
||||
AddColorToTree(tree, (const LICE_pixel_chan*)px);
|
||||
if (tree->leafcount > tree->maxcolors) PruneTree(tree);
|
||||
pxcnt++;
|
||||
}
|
||||
px++;
|
||||
}
|
||||
}
|
||||
|
||||
return pxcnt;
|
||||
}
|
||||
|
||||
|
||||
int LICE_BuildOctreeForDiff(void* octree, LICE_IBitmap* bmp, LICE_IBitmap* refbmp, LICE_pixel mask)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree || !bmp || !refbmp) return 0;
|
||||
|
||||
tree->palette_valid=false;
|
||||
|
||||
int y;
|
||||
const int h=lice_min(bmp->getHeight(),refbmp->getHeight());
|
||||
const int w=lice_min(bmp->getWidth(),refbmp->getWidth());
|
||||
|
||||
int rowspan = bmp->getRowSpan();
|
||||
int rowspan2 = refbmp->getRowSpan();
|
||||
const LICE_pixel *bits = bmp->getBits();
|
||||
const LICE_pixel *bits2 = refbmp->getBits();
|
||||
|
||||
if (bmp->isFlipped())
|
||||
{
|
||||
bits += rowspan * (bmp->getHeight()-1);
|
||||
rowspan = -rowspan;
|
||||
}
|
||||
|
||||
if (refbmp->isFlipped())
|
||||
{
|
||||
bits2 += rowspan2 * (refbmp->getHeight()-1);
|
||||
rowspan2 = -rowspan2;
|
||||
}
|
||||
|
||||
int pxcnt=0;
|
||||
for (y = 0; y < h; ++y)
|
||||
{
|
||||
const LICE_pixel * px = bits+y*rowspan;
|
||||
const LICE_pixel * px2 = bits2+y*rowspan2;
|
||||
int x=w;
|
||||
while (x--)
|
||||
{
|
||||
if ((*px ^ *px2) & mask)
|
||||
{
|
||||
AddColorToTree(tree, (const LICE_pixel_chan *)px);
|
||||
if (tree->leafcount > tree->maxcolors) PruneTree(tree);
|
||||
pxcnt++;
|
||||
}
|
||||
px++;
|
||||
px2++;
|
||||
}
|
||||
}
|
||||
|
||||
return pxcnt;
|
||||
}
|
||||
|
||||
|
||||
int LICE_FindInOctree(void* octree, LICE_pixel color)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree) return 0;
|
||||
|
||||
if (!tree->palette_valid) CollectLeaves(tree);
|
||||
|
||||
return FindColorInTree(tree, (const LICE_pixel_chan *)&color);
|
||||
}
|
||||
|
||||
|
||||
int LICE_ExtractOctreePalette(void* octree, LICE_pixel* palette)
|
||||
{
|
||||
OTree* tree = (OTree*)octree;
|
||||
if (!tree || !palette) return 0;
|
||||
|
||||
if (!tree->palette_valid) CollectLeaves(tree);
|
||||
|
||||
if (tree->palette) memcpy(palette, tree->palette, tree->maxcolors*sizeof(LICE_pixel));
|
||||
|
||||
return tree->leafcount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LICE_TestPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int numcolors)
|
||||
{
|
||||
int x, y;
|
||||
for (y = 0; y < bmp->getHeight(); ++y)
|
||||
{
|
||||
LICE_pixel* px = bmp->getBits()+y*bmp->getRowSpan();
|
||||
for (x = 0; x < bmp->getWidth(); ++x)
|
||||
{
|
||||
const LICE_pixel col = px[x];
|
||||
const int rgb[3] = { (int)LICE_GETR(col), (int)LICE_GETG(col), (int)LICE_GETB(col) };
|
||||
|
||||
int minerr = 0x7FFFFFFF;
|
||||
int bestcol=-1;
|
||||
int i;
|
||||
for (i = 0; i < numcolors; ++i)
|
||||
{
|
||||
const LICE_pixel palcol = palette[i];
|
||||
const int rerr[3] = { rgb[0]-(int)LICE_GETR(palcol), rgb[1]-(int)LICE_GETG(palcol), rgb[2]-(int)LICE_GETB(palcol) };
|
||||
const int err = rerr[0]*rerr[0]+rerr[1]*rerr[1]+rerr[2]*rerr[2];
|
||||
if (err < minerr)
|
||||
{
|
||||
bestcol=i;
|
||||
minerr=err;
|
||||
}
|
||||
}
|
||||
px[x] = palette[bestcol];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AddColorToTree(OTree* tree, const LICE_pixel_chan *rgb)
|
||||
{
|
||||
ONode* p = tree->trunk;
|
||||
p->colorcount++;
|
||||
|
||||
int i;
|
||||
const unsigned char r = rgb[LICE_PIXEL_R];
|
||||
const unsigned char g = rgb[LICE_PIXEL_G];
|
||||
const unsigned char b = rgb[LICE_PIXEL_B];
|
||||
for (i = OCTREE_DEPTH-1; i >= 0; --i)
|
||||
{
|
||||
const int j = i+8-OCTREE_DEPTH;
|
||||
const unsigned char idx = (((r>>(j-2))&4))|(((g>>(j-1))&2))|((b>>j)&1);
|
||||
|
||||
ONode* np = p->children[idx];
|
||||
bool isleaf = false;
|
||||
|
||||
if (np)
|
||||
{
|
||||
isleaf = !np->childflag;
|
||||
}
|
||||
else // add node
|
||||
{
|
||||
if (!p->childflag) // first time down this path
|
||||
{
|
||||
p->childflag=idx+1;
|
||||
}
|
||||
else if (p->childflag > 0) // creating a new branch
|
||||
{
|
||||
p->childflag = -1;
|
||||
p->next = tree->branches[i];
|
||||
tree->branches[i] = p;
|
||||
}
|
||||
// else multiple branch, which we don't care about
|
||||
|
||||
np=tree->spares;
|
||||
if (np) tree->spares = np->next;
|
||||
else np = new ONode;
|
||||
|
||||
p->children[idx] = np;
|
||||
memset(np, 0, sizeof(ONode));
|
||||
}
|
||||
|
||||
np->sumrgb[0] += r;
|
||||
np->sumrgb[1] += g;
|
||||
np->sumrgb[2] += b;
|
||||
np->colorcount++;
|
||||
|
||||
if (isleaf) return;
|
||||
|
||||
p=np; // continue downward
|
||||
}
|
||||
|
||||
// p is a new leaf at the bottom
|
||||
tree->leafcount++;
|
||||
}
|
||||
|
||||
int FindColorInTree(OTree* tree, const LICE_pixel_chan *rgb)
|
||||
{
|
||||
ONode* p = tree->trunk;
|
||||
|
||||
int i;
|
||||
const unsigned char r=rgb[LICE_PIXEL_R];
|
||||
const unsigned char g=rgb[LICE_PIXEL_G];
|
||||
const unsigned char b=rgb[LICE_PIXEL_B];
|
||||
for (i = OCTREE_DEPTH-1; i >= 0; --i)
|
||||
{
|
||||
if (!p->childflag) break;
|
||||
|
||||
const int j = i+8-OCTREE_DEPTH;
|
||||
const unsigned char idx = (((r>>(j-2))&4))|(((g>>(j-1))&2))|((b>>j)&1);
|
||||
|
||||
ONode* np = p->children[idx];
|
||||
if (!np) break;
|
||||
|
||||
p = np;
|
||||
}
|
||||
|
||||
return p->leafidx;
|
||||
}
|
||||
|
||||
|
||||
int PruneTree(OTree* tree)
|
||||
{
|
||||
ONode* branch=0;
|
||||
int i;
|
||||
for (i = 0; i < OCTREE_DEPTH; ++i) // prune at the furthest level from the trunk
|
||||
{
|
||||
branch = tree->branches[i];
|
||||
if (branch)
|
||||
{
|
||||
tree->branches[i] = branch->next;
|
||||
branch->next=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (branch)
|
||||
{
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
if (branch->children[i])
|
||||
{
|
||||
DeleteNode(tree, branch->children[i],&tree->spares);
|
||||
branch->children[i]=0;
|
||||
}
|
||||
}
|
||||
branch->childflag=0; // now it's a leaf
|
||||
tree->leafcount++;
|
||||
}
|
||||
|
||||
return tree->leafcount;
|
||||
}
|
||||
|
||||
int CollectLeaves(OTree* tree)
|
||||
{
|
||||
if (!tree->palette) tree->palette = (LICE_pixel*)malloc(tree->maxcolors*sizeof(LICE_pixel));
|
||||
|
||||
if (!tree->palette) return 0;
|
||||
|
||||
int sz = CollectNodeLeaves(tree->trunk, tree->palette, 0);
|
||||
memset(tree->palette+sz, 0, (tree->maxcolors-sz)*sizeof(LICE_pixel));
|
||||
tree->palette_valid = true;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int CollectNodeLeaves(ONode* p, LICE_pixel* palette, int colorcount)
|
||||
{
|
||||
if (!p->childflag)
|
||||
{
|
||||
p->leafidx = colorcount;
|
||||
int r = (int)((double)p->sumrgb[0]/(double)p->colorcount);
|
||||
int g = (int)((double)p->sumrgb[1]/(double)p->colorcount);
|
||||
int b = (int)((double)p->sumrgb[2]/(double)p->colorcount);
|
||||
palette[colorcount++] = LICE_RGBA(r, g, b, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->childflag > 0)
|
||||
{
|
||||
colorcount = CollectNodeLeaves(p->children[p->childflag-1], palette, colorcount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
if (p->children[i])
|
||||
{
|
||||
colorcount = CollectNodeLeaves(p->children[i], palette, colorcount);
|
||||
}
|
||||
}
|
||||
}
|
||||
// this is a branch or passthrough node, record the index
|
||||
// of any downtree leaf here so that we can return it for
|
||||
// color lookups that want to diverge off this node
|
||||
p->leafidx = colorcount-1;
|
||||
}
|
||||
|
||||
// colorcount should == leafcount
|
||||
return colorcount;
|
||||
}
|
||||
|
||||
|
||||
void DeleteNode(OTree* tree, ONode* p, ONode **delete_to)
|
||||
{
|
||||
if (!p->childflag)
|
||||
{
|
||||
tree->leafcount--;
|
||||
}
|
||||
else if (p->childflag > 0)
|
||||
{
|
||||
DeleteNode(tree, p->children[p->childflag-1],delete_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
if (p->children[i])
|
||||
{
|
||||
DeleteNode(tree, p->children[i],delete_to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_to)
|
||||
{
|
||||
p->next = *delete_to;
|
||||
*delete_to = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user