- Added BMF (ByteMap Font) support. This was complicated somewhat by the fact that BMF can

specify a character advance separately from the glyph width. GetChar and GetCharWidth now
  return this value in place of the glyph width. (For non-BMF fonts, these should still
  return the same values as before.)

SVN r2180 (trunk)
This commit is contained in:
Randy Heit 2010-02-26 05:34:30 +00:00
parent 8ecafcc15a
commit d530fd7f90
4 changed files with 331 additions and 91 deletions

View file

@ -130,7 +130,10 @@ protected:
bool rescale, PalEntry *out_palette); bool rescale, PalEntry *out_palette);
void LoadFON1 (int lump, const BYTE *data); void LoadFON1 (int lump, const BYTE *data);
void LoadFON2 (int lump, const BYTE *data); void LoadFON2 (int lump, const BYTE *data);
void LoadBMF (int lump, const BYTE *data);
void CreateFontFromPic (FTextureID picnum); void CreateFontFromPic (FTextureID picnum);
static int STACK_ARGS BMFCompare(const void *a, const void *b);
}; };
class FSinglePicFont : public FFont class FSinglePicFont : public FFont
@ -175,7 +178,7 @@ protected:
class FFontChar2 : public FTexture class FFontChar2 : public FTexture
{ {
public: public:
FFontChar2 (int sourcelump, const BYTE *sourceremap, int sourcepos, int width, int height); FFontChar2 (int sourcelump, const BYTE *sourceremap, int sourcepos, int width, int height, int leftofs=0, int topofs=0);
~FFontChar2 (); ~FFontChar2 ();
const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetColumn (unsigned int column, const Span **spans_out);
@ -399,21 +402,26 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count,
if (charlumps[i] != NULL) if (charlumps[i] != NULL)
{ {
Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap); Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap);
Chars[i].XMove = Chars[i].Pic->GetScaledWidth();
} }
else else
{ {
Chars[i].Pic = NULL; Chars[i].Pic = NULL;
Chars[i].XMove = INT_MIN;
} }
} }
if ('N'-first>=0 && 'N'-first<count && Chars['N' - first].Pic) if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].Pic != NULL)
{ {
SpaceWidth = (Chars['N' - first].Pic->GetScaledWidth() + 1) / 2; SpaceWidth = (Chars['N' - first].XMove + 1) / 2;
} }
else else
{ {
SpaceWidth = 4; SpaceWidth = 4;
} }
FixXMoves();
BuildTranslations (luminosity, identity, &TranslationParms[0][0], ActiveColors, NULL); BuildTranslations (luminosity, identity, &TranslationParms[0][0], ActiveColors, NULL);
delete[] luminosity; delete[] luminosity;
@ -717,6 +725,33 @@ FRemapTable *FFont::GetColorTranslation (EColorRange range) const
return &Ranges[range]; return &Ranges[range];
} }
//==========================================================================
//
// FFont :: GetCharCode
//
// If the character code is in the font, returns it. If it is not, but it
// is lowercase and has an uppercase variant present, return that. Otherwise
// return -1.
//
//==========================================================================
int FFont::GetCharCode(int code, bool needpic) const
{
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].Pic != NULL))
{
return code;
}
if (myislower[code])
{
code -= 32;
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].Pic != NULL))
{
return code;
}
}
return -1;
}
//========================================================================== //==========================================================================
// //
// FFont :: GetChar // FFont :: GetChar
@ -725,31 +760,28 @@ FRemapTable *FFont::GetColorTranslation (EColorRange range) const
FTexture *FFont::GetChar (int code, int *const width) const FTexture *FFont::GetChar (int code, int *const width) const
{ {
if (code < FirstChar || code = GetCharCode(code, false);
code > LastChar || int xmove = SpaceWidth;
Chars[code - FirstChar].Pic == NULL)
if (code >= 0)
{ {
if (myislower[code]) code -= FirstChar;
xmove = Chars[code].XMove;
if (Chars[code].Pic == NULL)
{ {
code -= 32; code = GetCharCode(code + FirstChar, true);
if (code < FirstChar || if (code >= 0)
code > LastChar ||
Chars[code - FirstChar].Pic == NULL)
{ {
if (width != NULL) *width = SpaceWidth; code -= FirstChar;
return NULL; xmove = Chars[code].XMove;
} }
} }
else
{
if (width != NULL) *width = SpaceWidth;
return NULL;
}
} }
if (width != NULL)
code -= FirstChar; {
if (width != NULL) *width = Chars[code].Pic->GetScaledWidth(); *width = xmove;
return Chars[code].Pic; }
return (code < 0) ? NULL : Chars[code].Pic;
} }
//========================================================================== //==========================================================================
@ -760,27 +792,8 @@ FTexture *FFont::GetChar (int code, int *const width) const
int FFont::GetCharWidth (int code) const int FFont::GetCharWidth (int code) const
{ {
if (code < FirstChar || code = GetCharCode(code, false);
code > LastChar || return (code < 0) ? SpaceWidth : Chars[code - FirstChar].XMove;
Chars[code - FirstChar].Pic == NULL)
{
if (myislower[code])
{
code -= 32;
if (code < FirstChar ||
code > LastChar ||
Chars[code - FirstChar].Pic == NULL)
{
return SpaceWidth;
}
}
else
{
return SpaceWidth;
}
}
return Chars[code - FirstChar].Pic->GetScaledWidth();
} }
//========================================================================== //==========================================================================
@ -858,7 +871,11 @@ FSingleLumpFont::FSingleLumpFont (const char *name, int lump)
FMemLump data1 = Wads.ReadLump (lump); FMemLump data1 = Wads.ReadLump (lump);
const BYTE *data = (const BYTE *)data1.GetMem(); const BYTE *data = (const BYTE *)data1.GetMem();
if (data[0] != 'F' || data[1] != 'O' || data[2] != 'N' || if (data[0] == 0xE1 && data[1] == 0xE6 && data[2] == 0xD5 && data[3] == 0x1A)
{
LoadBMF(lump, data);
}
else if (data[0] != 'F' || data[1] != 'O' || data[2] != 'N' ||
(data[3] != '1' && data[3] != '2')) (data[3] != '1' && data[3] != '2'))
{ {
I_FatalError ("%s is not a recognizable font", name); I_FatalError ("%s is not a recognizable font", name);
@ -1012,6 +1029,7 @@ void FSingleLumpFont::LoadFON2 (int lump, const BYTE *data)
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
{ {
int destSize = widths2[i] * FontHeight; int destSize = widths2[i] * FontHeight;
Chars[i].XMove = widths2[i];
if (destSize <= 0) if (destSize <= 0)
{ {
Chars[i].Pic = NULL; Chars[i].Pic = NULL;
@ -1045,6 +1063,157 @@ void FSingleLumpFont::LoadFON2 (int lump, const BYTE *data)
delete[] widths2; delete[] widths2;
} }
//==========================================================================
//
// FSingleLumpFont :: LoadBMF
//
// Loads a BMF font. The file format is described at
// <http://bmf.wz.cz/bmf-format.htm>
//
//==========================================================================
void FSingleLumpFont::LoadBMF(int lump, const BYTE *data)
{
const BYTE *chardata;
int numchars, count, totalwidth, nwidth;
int infolen;
int i, chari;
BYTE raw_palette[256*3];
PalEntry sort_palette[256];
PalEntry local_palette[256];
double luminosity[256];
BYTE identity[256];
FontHeight = data[5];
GlobalKerning = (SBYTE)data[8];
ActiveColors = data[16];
SpaceWidth = -1;
nwidth = -1;
infolen = data[17 + ActiveColors*3];
chardata = data + 18 + ActiveColors*3 + infolen;
numchars = chardata[0] + 256*chardata[1];
chardata += 2;
// Scan for lowest and highest characters defined and total font width.
FirstChar = 256;
LastChar = 0;
totalwidth = 0;
for (i = chari = 0; i < numchars; ++i, chari += 6 + chardata[chari+1] * chardata[chari+2])
{
if ((chardata[chari+1] == 0 || chardata[chari+2] == 0) && chardata[chari+5] == 0)
{ // Don't count empty characters.
continue;
}
if (chardata[chari] < FirstChar)
{
FirstChar = chardata[chari];
}
if (chardata[chari] > LastChar)
{
LastChar = chardata[chari];
}
totalwidth += chardata[chari+1];
}
if (LastChar < FirstChar)
{
I_FatalError("BMF font defines no characters");
}
count = LastChar - FirstChar + 1;
Chars = new CharData[count];
for (i = 0; i < count; ++i)
{
Chars[i].Pic = NULL;
Chars[i].XMove = INT_MIN;
}
// BMF palettes are only six bits per component. Fix that.
for (i = 0; i < ActiveColors*3; ++i)
{
raw_palette[i] = (data[17 + i] << 2) | (data[17 + i] >> 4);
}
// Sort the palette by increasing brightness
for (i = 0; i < ActiveColors; ++i)
{
PalEntry *pal = &sort_palette[i];
pal->a = i; // Use alpha part to point back to original entry
pal->r = raw_palette[i*3 + 0];
pal->g = raw_palette[i*3 + 1];
pal->b = raw_palette[i*3 + 2];
}
qsort(sort_palette + 1, ActiveColors - 1, sizeof(PalEntry), BMFCompare);
// Create the PatchRemap table from the sorted "alpha" values.
PatchRemap = new BYTE[ActiveColors];
PatchRemap[0] = 0;
for (i = 1; i < ActiveColors; ++i)
{
PatchRemap[sort_palette[i].a] = i;
}
FixupPalette(identity, luminosity, raw_palette, true, local_palette);
// Now scan through the characters again, creating glyphs for each one.
for (i = chari = 0; i < numchars; ++i, chari += 6 + chardata[chari+1] * chardata[chari+2])
{
assert(chardata[chari] - FirstChar >= 0);
assert(chardata[chari] - FirstChar < count);
if (chardata[chari] == ' ')
{
SpaceWidth = chardata[chari+5];
}
else if (chardata[chari] == 'N')
{
nwidth = chardata[chari+5];
}
Chars[chardata[chari] - FirstChar].XMove = chardata[chari+5];
if (chardata[chari+1] == 0 || chardata[chari+2] == 0)
{ // Empty character: skip it.
continue;
}
Chars[chardata[chari] - FirstChar].Pic = new FFontChar2(lump, PatchRemap, int(chardata + chari + 6 - data),
chardata[chari+1], // width
chardata[chari+2], // height
-(SBYTE)chardata[chari+3], // x offset
-(SBYTE)chardata[chari+4] // y offset
);
}
// If the font did not define a space character, determine a suitable space width now.
if (SpaceWidth < 0)
{
if (nwidth >= 0)
{
SpaceWidth = nwidth;
}
else
{
SpaceWidth = totalwidth * 2 / (3 * count);
}
}
FixXMoves();
BuildTranslations(luminosity, identity, &TranslationParms[0][0], ActiveColors, local_palette);
}
//==========================================================================
//
// FSingleLumpFont :: BMFCompare STATIC
//
// Helper to sort BMF palettes.
//
//==========================================================================
int STACK_ARGS FSingleLumpFont::BMFCompare(const void *a, const void *b)
{
const PalEntry *pa = (const PalEntry *)a;
const PalEntry *pb = (const PalEntry *)b;
return (pa->r * 299 + pa->g * 587 + pa->b * 114) -
(pb->r * 299 + pb->g * 587 + pb->b * 114);
}
//========================================================================== //==========================================================================
// //
// FSingleLumpFont :: CheckFON1Chars // FSingleLumpFont :: CheckFON1Chars
@ -1069,6 +1238,7 @@ void FSingleLumpFont::CheckFON1Chars (int lump, const BYTE *data, double *lumino
int destSize = SpaceWidth * FontHeight; int destSize = SpaceWidth * FontHeight;
Chars[i].Pic = new FFontChar2 (lump, PatchRemap, int(data_p - data), SpaceWidth, FontHeight); Chars[i].Pic = new FFontChar2 (lump, PatchRemap, int(data_p - data), SpaceWidth, FontHeight);
Chars[i].XMove = SpaceWidth;
// Advance to next char's data and count the used colors. // Advance to next char's data and count the used colors.
do do
@ -1332,14 +1502,14 @@ FFontChar1::~FFontChar1 ()
// //
//========================================================================== //==========================================================================
FFontChar2::FFontChar2 (int sourcelump, const BYTE *sourceremap, int sourcepos, int width, int height) FFontChar2::FFontChar2 (int sourcelump, const BYTE *sourceremap, int sourcepos, int width, int height, int leftofs, int topofs)
: SourceLump (sourcelump), SourcePos (sourcepos), Pixels (0), Spans (0), SourceRemap(sourceremap) : SourceLump (sourcelump), SourcePos (sourcepos), Pixels (0), Spans (0), SourceRemap(sourceremap)
{ {
UseType = TEX_FontChar; UseType = TEX_FontChar;
Width = width; Width = width;
Height = height; Height = height;
TopOffset = 0; LeftOffset = leftofs;
LeftOffset = 0; TopOffset = topofs;
CalcBitSize (); CalcBitSize ();
} }
@ -1427,10 +1597,11 @@ void FFontChar2::MakeTexture ()
FWadLump lump = Wads.OpenLumpNum (SourceLump); FWadLump lump = Wads.OpenLumpNum (SourceLump);
int destSize = Width * Height; int destSize = Width * Height;
BYTE max = 255; BYTE max = 255;
bool rle = true;
// This is to "fix" bad fonts // This is to "fix" bad fonts
{ {
BYTE buff[8]; BYTE buff[16];
lump.Read (buff, 4); lump.Read (buff, 4);
if (buff[3] == '2') if (buff[3] == '2')
{ {
@ -1438,6 +1609,13 @@ void FFontChar2::MakeTexture ()
max = buff[6]; max = buff[6];
lump.Seek (SourcePos - 11, SEEK_CUR); lump.Seek (SourcePos - 11, SEEK_CUR);
} }
else if (buff[3] == 0x1A)
{
lump.Read(buff, 13);
max = buff[12] - 1;
lump.Seek (SourcePos - 17, SEEK_CUR);
rle = false;
}
else else
{ {
lump.Seek (SourcePos - 4, SEEK_CUR); lump.Seek (SourcePos - 4, SEEK_CUR);
@ -1452,55 +1630,81 @@ void FFontChar2::MakeTexture ()
int dest_adv = Height; int dest_adv = Height;
int dest_rew = destSize - 1; int dest_rew = destSize - 1;
for (int y = Height; y != 0; --y) if (rle)
{ {
for (int x = Width; x != 0; ) for (int y = Height; y != 0; --y)
{ {
if (runlen != 0) for (int x = Width; x != 0; )
{ {
BYTE color; if (runlen != 0)
lump >> color;
*dest_p = MIN (color, max);
if (SourceRemap != NULL)
{
*dest_p = SourceRemap[*dest_p];
}
dest_p += dest_adv;
x--;
runlen--;
}
else if (setlen != 0)
{
*dest_p = setval;
dest_p += dest_adv;
x--;
setlen--;
}
else
{
SBYTE code;
lump >> code;
if (code >= 0)
{
runlen = code + 1;
}
else if (code != -128)
{ {
BYTE color; BYTE color;
lump >> color; lump >> color;
setlen = (-code) + 1; color = MIN (color, max);
setval = MIN (color, max);
if (SourceRemap != NULL) if (SourceRemap != NULL)
{ {
setval = SourceRemap[setval]; color = SourceRemap[color];
}
*dest_p = color;
dest_p += dest_adv;
x--;
runlen--;
}
else if (setlen != 0)
{
*dest_p = setval;
dest_p += dest_adv;
x--;
setlen--;
}
else
{
SBYTE code;
lump >> code;
if (code >= 0)
{
runlen = code + 1;
}
else if (code != -128)
{
BYTE color;
lump >> color;
setlen = (-code) + 1;
setval = MIN (color, max);
if (SourceRemap != NULL)
{
setval = SourceRemap[setval];
}
} }
} }
} }
dest_p -= dest_rew;
}
}
else
{
for (int y = Height; y != 0; --y)
{
for (int x = Width; x != 0; --x)
{
BYTE color;
lump >> color;
if (color > max)
{
color = max;
}
if (SourceRemap != NULL)
{
color = SourceRemap[color];
}
*dest_p = color;
dest_p += dest_adv;
}
dest_p -= dest_rew;
} }
dest_p -= dest_rew;
} }
if (destSize < 0) if (destSize < 0)
@ -1593,23 +1797,27 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l
if (charlumps[i] != NULL) if (charlumps[i] != NULL)
{ {
Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap); Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap);
Chars[i].XMove = Chars[i].Pic->GetScaledWidth();
} }
else else
{ {
Chars[i].Pic = NULL; Chars[i].Pic = NULL;
Chars[i].XMove = INT_MIN;
} }
} }
// Special fonts normally don't have all characters so be careful here! // Special fonts normally don't have all characters so be careful here!
if ('N'-first>=0 && 'N'-first<count && Chars['N' - first].Pic) if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].Pic != NULL)
{ {
SpaceWidth = (Chars['N' - first].Pic->GetScaledWidth() + 1) / 2; SpaceWidth = (Chars['N' - first].XMove + 1) / 2;
} }
else else
{ {
SpaceWidth = 4; SpaceWidth = 4;
} }
FixXMoves();
BuildTranslations (luminosity, identity, &TranslationParms[0][0], TotalColors, NULL); BuildTranslations (luminosity, identity, &TranslationParms[0][0], TotalColors, NULL);
// add the untranslated colors to the Ranges tables // add the untranslated colors to the Ranges tables
@ -1632,6 +1840,36 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l
delete[] charlumps; delete[] charlumps;
} }
//==========================================================================
//
// FFont :: FixXMoves
//
// If a font has gaps in its characters, set the missing characters'
// XMoves to either SpaceWidth or the uppercase variant's XMove. Missing
// XMoves must be initialized with INT_MIN beforehand.
//
//==========================================================================
void FFont::FixXMoves()
{
for (int i = 0; i <= LastChar - FirstChar; ++i)
{
if (Chars[i].XMove == INT_MIN)
{
if (myislower[i + FirstChar])
{
int upper = i - 32;
if (upper >= 0)
{
Chars[i].XMove = Chars[upper].XMove;
continue;
}
}
Chars[i].XMove = SpaceWidth;
}
}
}
//========================================================================== //==========================================================================
// //

View file

@ -94,11 +94,14 @@ public:
int StringWidth (const BYTE *str) const; int StringWidth (const BYTE *str) const;
inline int StringWidth (const char *str) const { return StringWidth ((const BYTE *)str); } inline int StringWidth (const char *str) const { return StringWidth ((const BYTE *)str); }
int GetCharCode(int code, bool needpic) const;
protected: protected:
FFont (); FFont ();
void BuildTranslations (const double *luminosity, const BYTE *identity, void BuildTranslations (const double *luminosity, const BYTE *identity,
const void *ranges, int total_colors, const PalEntry *palette); const void *ranges, int total_colors, const PalEntry *palette);
void FixXMoves();
static int SimpleTranslation (BYTE *colorsused, BYTE *translation, static int SimpleTranslation (BYTE *colorsused, BYTE *translation,
BYTE *identity, double **luminosity); BYTE *identity, double **luminosity);
@ -110,6 +113,7 @@ protected:
struct CharData struct CharData
{ {
FTexture *Pic; FTexture *Pic;
int XMove;
} *Chars; } *Chars;
int ActiveColors; int ActiveColors;
TArray<FRemapTable> Ranges; TArray<FRemapTable> Ranges;

View file

@ -1935,15 +1935,13 @@ void WI_loadData(void)
} }
else else
{ {
int dummywidth; star = BigFont->GetChar('*', NULL);
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
bstar = star; bstar = star;
} }
} }
else // Strife needs some handling, too! else // Strife needs some handling, too!
{ {
int dummywidth; star = BigFont->GetChar('*', NULL);
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
bstar = star; bstar = star;
} }

View file

@ -1291,7 +1291,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE nothing, LPSTR cmdline, int n
_CrtSetDbgFlag (_CrtSetDbgFlag(0) | _CRTDBG_LEAK_CHECK_DF); _CrtSetDbgFlag (_CrtSetDbgFlag(0) | _CRTDBG_LEAK_CHECK_DF);
// Use this to break at a specific allocation number. // Use this to break at a specific allocation number.
//_crtBreakAlloc = 3660; //_crtBreakAlloc = 30055;
#endif #endif
DoMain (hInstance); DoMain (hInstance);