mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-10 23:01:50 +00:00
- Removed all the "fast" and unused code from FColorMatcher. Today's
computers are fast enough that the difference isn't even noticeable unless you're doing hundreds of thousands of matches, and I never had any plans to improve the algorithm the "fast" code used. - Fixed: BestColor() should not by default return 1 as a possible match, since it's normally the transparent color. SVN r400 (trunk)
This commit is contained in:
parent
2dcc70dd31
commit
ca8765ed79
4 changed files with 14 additions and 261 deletions
|
@ -1,4 +1,10 @@
|
||||||
November 30, 2006
|
November 30, 2006
|
||||||
|
- Removed all the "fast" and unused code from FColorMatcher. Today's
|
||||||
|
computers are fast enough that the difference isn't even noticeable
|
||||||
|
unless you're doing hundreds of thousands of matches, and I never had
|
||||||
|
any plans to improve the algorithm the "fast" code used.
|
||||||
|
- Fixed: BestColor() should not by default return 1 as a possible match,
|
||||||
|
since it's normally the transparent color.
|
||||||
- The DSimpleCanvas constructor now fills MemBuffer with zeros.
|
- The DSimpleCanvas constructor now fills MemBuffer with zeros.
|
||||||
- Fixed: If the FBTexture wasn't exactly the same size as the screen,
|
- Fixed: If the FBTexture wasn't exactly the same size as the screen,
|
||||||
D3DFB::PaintToWindow() would still lock it with D3DLOCK_DISCARD. Alas,
|
D3DFB::PaintToWindow() would still lock it with D3DLOCK_DISCARD. Alas,
|
||||||
|
|
|
@ -30,8 +30,11 @@
|
||||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
**---------------------------------------------------------------------------
|
**---------------------------------------------------------------------------
|
||||||
**
|
**
|
||||||
** This tries to be a fast closest color finding system. It is, but the results
|
** Once upon a time, this tried to be a fast closest color finding system.
|
||||||
** are not as good as I would like, so I don't actually use it.
|
** It was, but the results were not as good as I would like, so I didn't
|
||||||
|
** actually use it. But I did keep the code around in case I ever felt like
|
||||||
|
** revisiting the problem. I never did, so now it's relegated to the mists
|
||||||
|
** of SVN history, and this is just a thin wrapper around BestColor().
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -40,29 +43,7 @@
|
||||||
|
|
||||||
#include "doomtype.h"
|
#include "doomtype.h"
|
||||||
#include "colormatcher.h"
|
#include "colormatcher.h"
|
||||||
#include "i_system.h"
|
#include "v_palette.h"
|
||||||
|
|
||||||
// Uncomment this to use the fast color lookup stuff.
|
|
||||||
// Unfortunately, it's not totally accurate. :-(
|
|
||||||
//#define BEFAST
|
|
||||||
|
|
||||||
struct FColorMatcher::Seed
|
|
||||||
{
|
|
||||||
BYTE r, g, b;
|
|
||||||
BYTE bad;
|
|
||||||
BYTE color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FColorMatcher::PalEntry
|
|
||||||
{
|
|
||||||
#ifndef WORDS_BIGENDIAN
|
|
||||||
BYTE b, g, r, a;
|
|
||||||
#else
|
|
||||||
BYTE a, r, g, b;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
extern int BestColor (const uint32 *palette, int r, int g, int b, int first = 0, int num = 256);
|
|
||||||
|
|
||||||
FColorMatcher::FColorMatcher ()
|
FColorMatcher::FColorMatcher ()
|
||||||
{
|
{
|
||||||
|
@ -82,224 +63,18 @@ FColorMatcher::FColorMatcher (const FColorMatcher &other)
|
||||||
FColorMatcher &FColorMatcher::operator= (const FColorMatcher &other)
|
FColorMatcher &FColorMatcher::operator= (const FColorMatcher &other)
|
||||||
{
|
{
|
||||||
Pal = other.Pal;
|
Pal = other.Pal;
|
||||||
memcpy (FirstColor, other.FirstColor, sizeof(FirstColor));
|
|
||||||
memcpy (NextColor, other.NextColor, sizeof(NextColor));
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FColorMatcher::SetPalette (const DWORD *palette)
|
void FColorMatcher::SetPalette (const DWORD *palette)
|
||||||
{
|
{
|
||||||
Pal = (const PalEntry *)palette;
|
Pal = (const PalEntry *)palette;
|
||||||
|
|
||||||
// 0 is the transparent color, so it is never a valid color
|
|
||||||
memset (FirstColor, 0, sizeof(FirstColor));
|
|
||||||
memset (NextColor, 0, sizeof(NextColor));
|
|
||||||
|
|
||||||
#ifdef BEFAST
|
|
||||||
Seed seeds[255];
|
|
||||||
BYTE seedspread[CHISIZE+1][CHISIZE+1][CHISIZE+1];
|
|
||||||
int numseeds;
|
|
||||||
int i, radius;
|
|
||||||
|
|
||||||
memset (seedspread, 255, sizeof(seedspread));
|
|
||||||
numseeds = 0;
|
|
||||||
|
|
||||||
// Plant each color from the palette as seeds in the color cube
|
|
||||||
for (i = 1; i < 256; i++)
|
|
||||||
{
|
|
||||||
int r = (Pal[i].r + CLOSIZE/2) >> CLOBITS;
|
|
||||||
int g = (Pal[i].g + CLOSIZE/2) >> CLOBITS;
|
|
||||||
int b = (Pal[i].b + CLOSIZE/2) >> CLOBITS;
|
|
||||||
|
|
||||||
if (FirstColor[r][g][b] == 0)
|
|
||||||
{
|
|
||||||
seeds[numseeds].r = r;
|
|
||||||
seeds[numseeds].g = g;
|
|
||||||
seeds[numseeds].b = b;
|
|
||||||
seedspread[r][g][b] = numseeds;
|
|
||||||
numseeds++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NextColor[i] = FirstColor[r][g][b];
|
|
||||||
}
|
|
||||||
FirstColor[r][g][b] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = numseeds-1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
seeds[i].color = FirstColor[seeds[i].r][seeds[i].g][seeds[i].b];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grow each seed outward as a cube until no seed can
|
|
||||||
// grow any further.
|
|
||||||
for (radius = 1; radius < CHISIZE; radius++)
|
|
||||||
{
|
|
||||||
int seedsused = 0;
|
|
||||||
for (i = numseeds - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (seeds[i].color == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
seedsused++;
|
|
||||||
/* _ */
|
|
||||||
int numhits = 0; /* _/| b (0,0,HISIZE) */
|
|
||||||
/* _/ */
|
|
||||||
int r1, r2, g1, g2, b1, b2; /* _/ */
|
|
||||||
/* / */
|
|
||||||
r1 = seeds[i].r - radius; /* +-------> r (HISIZE,0,0) */
|
|
||||||
r2 = seeds[i].r + radius; /* |(0,0,0) */
|
|
||||||
g1 = seeds[i].g - radius; /* | */
|
|
||||||
g2 = seeds[i].g + radius; /* | */
|
|
||||||
b1 = seeds[i].b - radius; /* | */
|
|
||||||
b2 = seeds[i].b + radius; /* v g (0,HISIZE,0) */
|
|
||||||
|
|
||||||
// Check to see which planes are acceptable
|
|
||||||
BYTE bad = 0;
|
|
||||||
if (r1 < 0) bad |= 1, r1 = 0;
|
|
||||||
if (r2 > CHISIZE) bad |= 2, r2 = CHISIZE;
|
|
||||||
if (g1 < 0) bad |= 4, g1 = 0;
|
|
||||||
if (g2 > CHISIZE) bad |= 8, g2 = CHISIZE;
|
|
||||||
if (b1 < 0) bad |= 16, b1 = 0;
|
|
||||||
if (b2 > CHISIZE) bad |= 32, b2 = CHISIZE;
|
|
||||||
|
|
||||||
bad |= seeds[i].bad;
|
|
||||||
|
|
||||||
if (!(bad & 1)) // Do left green-blue plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r1, r1, g1, g2, b1, b2, seedspread, seeds, i))
|
|
||||||
bad |= 1;
|
|
||||||
r1++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bad & 2)) // Do right green-blue plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r2, r2, g1, g2, b1, b2, seedspread, seeds, i))
|
|
||||||
bad |= 2;
|
|
||||||
r2--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bad & 4)) // Do top red-blue plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r1, r2, g1, g1, b1, b2, seedspread, seeds, i))
|
|
||||||
bad |= 4;
|
|
||||||
g1++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bad & 8)) // Do bottom red-blue plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r1, r2, g2, g2, b1, b2, seedspread, seeds, i))
|
|
||||||
bad |= 8;
|
|
||||||
g2--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bad & 16)) // Do front red-green plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r1, r2, g1, g2, b1, b1, seedspread, seeds, i))
|
|
||||||
bad |= 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bad & 32)) // Do back red-green plane
|
|
||||||
{
|
|
||||||
if (!FillPlane (r1, r2, g1, g2, b2, b2, seedspread, seeds, i))
|
|
||||||
bad |= 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bad == 63)
|
|
||||||
{ // This seed did not grow any further, so it can be deactivated
|
|
||||||
seeds[i].color = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // Remember which directions were blocked, so we don't
|
|
||||||
// try growing in those directions again in later passes.
|
|
||||||
seeds[i].bad = bad;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seedsused == 0)
|
|
||||||
{ // No seeds grew during this pass, so we're done.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int FColorMatcher::FillPlane (int r1, int r2, int g1, int g2, int b1, int b2,
|
|
||||||
BYTE seedspread[CHISIZE+1][CHISIZE+1][CHISIZE+1],
|
|
||||||
Seed *seeds, int thisseed)
|
|
||||||
{
|
|
||||||
const Seed *secnd = seeds + thisseed;
|
|
||||||
BYTE color = secnd->color;
|
|
||||||
int r, g, b;
|
|
||||||
int numhits = 0;
|
|
||||||
|
|
||||||
for (r = r1; r <= r2; r++)
|
|
||||||
{
|
|
||||||
for (g = g1; g <= g2; g++)
|
|
||||||
{
|
|
||||||
for (b = b1; b <= b2; b++)
|
|
||||||
{
|
|
||||||
if (seedspread[r][g][b] == 255)
|
|
||||||
{
|
|
||||||
seedspread[r][g][b] = thisseed;
|
|
||||||
FirstColor[r][g][b] = color;
|
|
||||||
numhits++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const Seed *first = seeds + seedspread[r][g][b];
|
|
||||||
|
|
||||||
int fr = r-first->r;
|
|
||||||
int fg = g-first->g;
|
|
||||||
int fb = b-first->b;
|
|
||||||
int sr = r-secnd->r;
|
|
||||||
int sg = g-secnd->g;
|
|
||||||
int sb = b-secnd->b;
|
|
||||||
if (fr*fr+fg*fg+fb*fb > sr*sr+sg*sg+sb*sb)
|
|
||||||
{
|
|
||||||
FirstColor[r][g][b] = color;
|
|
||||||
seedspread[r][g][b] = thisseed;
|
|
||||||
numhits++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numhits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BYTE FColorMatcher::Pick (int r, int g, int b)
|
BYTE FColorMatcher::Pick (int r, int g, int b)
|
||||||
{
|
{
|
||||||
if (Pal == NULL)
|
if (Pal == NULL)
|
||||||
return 0;
|
return 1;
|
||||||
|
|
||||||
#ifdef BEFAST
|
|
||||||
BYTE bestcolor;
|
|
||||||
int bestdist;
|
|
||||||
|
|
||||||
BYTE color = FirstColor[(r+CLOSIZE/2)>>CLOBITS][(g+CLOSIZE/2)>>CLOBITS][(b+CLOSIZE/2)>>CLOBITS];
|
|
||||||
if (NextColor[color] == 0)
|
|
||||||
return color;
|
|
||||||
|
|
||||||
bestcolor = 0;
|
|
||||||
bestdist = 257*257+257*257+257*257;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
int dist = (r-Pal[color].r)*(r-Pal[color].r)+
|
|
||||||
(g-Pal[color].g)*(g-Pal[color].g)+
|
|
||||||
(b-Pal[color].b)*(b-Pal[color].b);
|
|
||||||
if (dist < bestdist)
|
|
||||||
{
|
|
||||||
if (dist == 0)
|
|
||||||
return color;
|
|
||||||
|
|
||||||
bestdist = dist;
|
|
||||||
bestcolor = color;
|
|
||||||
}
|
|
||||||
color = NextColor[color];
|
|
||||||
} while (color != 0);
|
|
||||||
return bestcolor;
|
|
||||||
#else
|
|
||||||
return BestColor ((uint32 *)Pal, r, g, b);
|
return BestColor ((uint32 *)Pal, r, g, b);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,24 +34,6 @@
|
||||||
#ifndef __COLORMATCHER_H__
|
#ifndef __COLORMATCHER_H__
|
||||||
#define __COLORMATCHER_H__
|
#define __COLORMATCHER_H__
|
||||||
|
|
||||||
// This class implements a "pretty good" color matcher for palettized images.
|
|
||||||
// In many cases, it is able to choose the "best" color with only a few
|
|
||||||
// comparisons. In all other cases, it chooses an "almost best" color in only
|
|
||||||
// a few comparisons. This is much faster than scanning the entire palette
|
|
||||||
// when you want to pick a color, so the somewhat lesser quality should be
|
|
||||||
// acceptable unless quality is the highest priority.
|
|
||||||
//
|
|
||||||
// The reason for the lesser quality is because this algorithm stores groups
|
|
||||||
// of colors into cells in a color cube and then spreads those color groups
|
|
||||||
// around into neighboring cells. When asked to pick a color, it looks in a
|
|
||||||
// single cell and sees which of the colors stored in that cell is closest
|
|
||||||
// to the requested color. For borderline cases, the chosen color may not
|
|
||||||
// actually be the best color in the entire palette, but it is still close.
|
|
||||||
//
|
|
||||||
// The accuracy of this class depends on the input palette and on HIBITS.
|
|
||||||
// HIBITS 4 seems to be a reasonable compromise between preprocessing time,
|
|
||||||
// space, and accuracy.
|
|
||||||
|
|
||||||
class FColorMatcher
|
class FColorMatcher
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -64,17 +46,7 @@ public:
|
||||||
FColorMatcher &operator= (const FColorMatcher &other);
|
FColorMatcher &operator= (const FColorMatcher &other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum { CHIBITS=4, CLOBITS=8-CHIBITS, CHISIZE=1<<CHIBITS, CLOSIZE=1<<CLOBITS};
|
|
||||||
struct Seed;
|
|
||||||
struct PalEntry;
|
|
||||||
|
|
||||||
const PalEntry *Pal;
|
const PalEntry *Pal;
|
||||||
BYTE FirstColor[CHISIZE+1][CHISIZE+1][CHISIZE+1];
|
|
||||||
BYTE NextColor[256];
|
|
||||||
|
|
||||||
int FillPlane (int r1, int r2, int g1, int g2, int b1, int b2,
|
|
||||||
BYTE seedspread[CHISIZE+1][CHISIZE+1][CHISIZE+1],
|
|
||||||
Seed *seeds, int thisseed);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //__COLORMATCHER_H__
|
#endif //__COLORMATCHER_H__
|
||||||
|
|
|
@ -87,7 +87,7 @@ extern FDynamicColormap NormalLight;
|
||||||
}
|
}
|
||||||
extern int Near255; // A color near 255 in appearance, but not 255
|
extern int Near255; // A color near 255 in appearance, but not 255
|
||||||
|
|
||||||
int BestColor (const uint32 *pal, int r, int g, int b, int first = 0, int num=256);
|
int BestColor (const uint32 *pal, int r, int g, int b, int first=1, int num=255);
|
||||||
|
|
||||||
void InitPalette ();
|
void InitPalette ();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue