mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-15 16:51:31 +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
|
||||
- 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.
|
||||
- Fixed: If the FBTexture wasn't exactly the same size as the screen,
|
||||
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 tries to be a fast closest color finding system. It is, but the results
|
||||
** are not as good as I would like, so I don't actually use it.
|
||||
** Once upon a time, this tried to be a fast closest color finding system.
|
||||
** 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 "colormatcher.h"
|
||||
#include "i_system.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);
|
||||
#include "v_palette.h"
|
||||
|
||||
FColorMatcher::FColorMatcher ()
|
||||
{
|
||||
|
@ -82,224 +63,18 @@ FColorMatcher::FColorMatcher (const FColorMatcher &other)
|
|||
FColorMatcher &FColorMatcher::operator= (const FColorMatcher &other)
|
||||
{
|
||||
Pal = other.Pal;
|
||||
memcpy (FirstColor, other.FirstColor, sizeof(FirstColor));
|
||||
memcpy (NextColor, other.NextColor, sizeof(NextColor));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void FColorMatcher::SetPalette (const DWORD *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)
|
||||
{
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -34,24 +34,6 @@
|
|||
#ifndef __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
|
||||
{
|
||||
public:
|
||||
|
@ -64,17 +46,7 @@ public:
|
|||
FColorMatcher &operator= (const FColorMatcher &other);
|
||||
|
||||
private:
|
||||
enum { CHIBITS=4, CLOBITS=8-CHIBITS, CHISIZE=1<<CHIBITS, CLOSIZE=1<<CLOBITS};
|
||||
struct Seed;
|
||||
struct PalEntry;
|
||||
|
||||
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__
|
||||
|
|
|
@ -87,7 +87,7 @@ extern FDynamicColormap NormalLight;
|
|||
}
|
||||
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 ();
|
||||
|
||||
|
|
Loading…
Reference in a new issue