mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-11 15:21:51 +00:00
- 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:
parent
8ecafcc15a
commit
d530fd7f90
4 changed files with 331 additions and 91 deletions
350
src/v_font.cpp
350
src/v_font.cpp
|
@ -130,7 +130,10 @@ protected:
|
|||
bool rescale, PalEntry *out_palette);
|
||||
void LoadFON1 (int lump, const BYTE *data);
|
||||
void LoadFON2 (int lump, const BYTE *data);
|
||||
void LoadBMF (int lump, const BYTE *data);
|
||||
void CreateFontFromPic (FTextureID picnum);
|
||||
|
||||
static int STACK_ARGS BMFCompare(const void *a, const void *b);
|
||||
};
|
||||
|
||||
class FSinglePicFont : public FFont
|
||||
|
@ -175,7 +178,7 @@ protected:
|
|||
class FFontChar2 : public FTexture
|
||||
{
|
||||
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 ();
|
||||
|
||||
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)
|
||||
{
|
||||
Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap);
|
||||
Chars[i].XMove = Chars[i].Pic->GetScaledWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
{
|
||||
SpaceWidth = 4;
|
||||
}
|
||||
|
||||
FixXMoves();
|
||||
|
||||
BuildTranslations (luminosity, identity, &TranslationParms[0][0], ActiveColors, NULL);
|
||||
|
||||
delete[] luminosity;
|
||||
|
@ -717,6 +725,33 @@ FRemapTable *FFont::GetColorTranslation (EColorRange range) const
|
|||
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
|
||||
|
@ -725,31 +760,28 @@ FRemapTable *FFont::GetColorTranslation (EColorRange range) const
|
|||
|
||||
FTexture *FFont::GetChar (int code, int *const width) const
|
||||
{
|
||||
if (code < FirstChar ||
|
||||
code > LastChar ||
|
||||
Chars[code - FirstChar].Pic == NULL)
|
||||
{
|
||||
if (myislower[code])
|
||||
{
|
||||
code -= 32;
|
||||
if (code < FirstChar ||
|
||||
code > LastChar ||
|
||||
Chars[code - FirstChar].Pic == NULL)
|
||||
{
|
||||
if (width != NULL) *width = SpaceWidth;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (width != NULL) *width = SpaceWidth;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
code = GetCharCode(code, false);
|
||||
int xmove = SpaceWidth;
|
||||
|
||||
if (code >= 0)
|
||||
{
|
||||
code -= FirstChar;
|
||||
if (width != NULL) *width = Chars[code].Pic->GetScaledWidth();
|
||||
return Chars[code].Pic;
|
||||
xmove = Chars[code].XMove;
|
||||
if (Chars[code].Pic == NULL)
|
||||
{
|
||||
code = GetCharCode(code + FirstChar, true);
|
||||
if (code >= 0)
|
||||
{
|
||||
code -= FirstChar;
|
||||
xmove = Chars[code].XMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (width != NULL)
|
||||
{
|
||||
*width = xmove;
|
||||
}
|
||||
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
|
||||
{
|
||||
if (code < FirstChar ||
|
||||
code > LastChar ||
|
||||
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();
|
||||
code = GetCharCode(code, false);
|
||||
return (code < 0) ? SpaceWidth : Chars[code - FirstChar].XMove;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -858,7 +871,11 @@ FSingleLumpFont::FSingleLumpFont (const char *name, int lump)
|
|||
FMemLump data1 = Wads.ReadLump (lump);
|
||||
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'))
|
||||
{
|
||||
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)
|
||||
{
|
||||
int destSize = widths2[i] * FontHeight;
|
||||
Chars[i].XMove = widths2[i];
|
||||
if (destSize <= 0)
|
||||
{
|
||||
Chars[i].Pic = NULL;
|
||||
|
@ -1045,6 +1063,157 @@ void FSingleLumpFont::LoadFON2 (int lump, const BYTE *data)
|
|||
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
|
||||
|
@ -1069,6 +1238,7 @@ void FSingleLumpFont::CheckFON1Chars (int lump, const BYTE *data, double *lumino
|
|||
int destSize = 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.
|
||||
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)
|
||||
{
|
||||
UseType = TEX_FontChar;
|
||||
Width = width;
|
||||
Height = height;
|
||||
TopOffset = 0;
|
||||
LeftOffset = 0;
|
||||
LeftOffset = leftofs;
|
||||
TopOffset = topofs;
|
||||
CalcBitSize ();
|
||||
}
|
||||
|
||||
|
@ -1427,10 +1597,11 @@ void FFontChar2::MakeTexture ()
|
|||
FWadLump lump = Wads.OpenLumpNum (SourceLump);
|
||||
int destSize = Width * Height;
|
||||
BYTE max = 255;
|
||||
bool rle = true;
|
||||
|
||||
// This is to "fix" bad fonts
|
||||
{
|
||||
BYTE buff[8];
|
||||
BYTE buff[16];
|
||||
lump.Read (buff, 4);
|
||||
if (buff[3] == '2')
|
||||
{
|
||||
|
@ -1438,6 +1609,13 @@ void FFontChar2::MakeTexture ()
|
|||
max = buff[6];
|
||||
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
|
||||
{
|
||||
lump.Seek (SourcePos - 4, SEEK_CUR);
|
||||
|
@ -1452,6 +1630,8 @@ void FFontChar2::MakeTexture ()
|
|||
int dest_adv = Height;
|
||||
int dest_rew = destSize - 1;
|
||||
|
||||
if (rle)
|
||||
{
|
||||
for (int y = Height; y != 0; --y)
|
||||
{
|
||||
for (int x = Width; x != 0; )
|
||||
|
@ -1461,11 +1641,12 @@ void FFontChar2::MakeTexture ()
|
|||
BYTE color;
|
||||
|
||||
lump >> color;
|
||||
*dest_p = MIN (color, max);
|
||||
color = MIN (color, max);
|
||||
if (SourceRemap != NULL)
|
||||
{
|
||||
*dest_p = SourceRemap[*dest_p];
|
||||
color = SourceRemap[color];
|
||||
}
|
||||
*dest_p = color;
|
||||
dest_p += dest_adv;
|
||||
x--;
|
||||
runlen--;
|
||||
|
@ -1502,6 +1683,29 @@ void FFontChar2::MakeTexture ()
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (destSize < 0)
|
||||
{
|
||||
|
@ -1593,23 +1797,27 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l
|
|||
if (charlumps[i] != NULL)
|
||||
{
|
||||
Chars[i].Pic = new FFontChar1 (charlumps[i], PatchRemap);
|
||||
Chars[i].XMove = Chars[i].Pic->GetScaledWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
Chars[i].Pic = NULL;
|
||||
Chars[i].XMove = INT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
SpaceWidth = 4;
|
||||
}
|
||||
|
||||
FixXMoves();
|
||||
|
||||
BuildTranslations (luminosity, identity, &TranslationParms[0][0], TotalColors, NULL);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -94,11 +94,14 @@ public:
|
|||
int StringWidth (const BYTE *str) const;
|
||||
inline int StringWidth (const char *str) const { return StringWidth ((const BYTE *)str); }
|
||||
|
||||
int GetCharCode(int code, bool needpic) const;
|
||||
|
||||
protected:
|
||||
FFont ();
|
||||
|
||||
void BuildTranslations (const double *luminosity, const BYTE *identity,
|
||||
const void *ranges, int total_colors, const PalEntry *palette);
|
||||
void FixXMoves();
|
||||
|
||||
static int SimpleTranslation (BYTE *colorsused, BYTE *translation,
|
||||
BYTE *identity, double **luminosity);
|
||||
|
@ -110,6 +113,7 @@ protected:
|
|||
struct CharData
|
||||
{
|
||||
FTexture *Pic;
|
||||
int XMove;
|
||||
} *Chars;
|
||||
int ActiveColors;
|
||||
TArray<FRemapTable> Ranges;
|
||||
|
|
|
@ -1935,15 +1935,13 @@ void WI_loadData(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
int dummywidth;
|
||||
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
|
||||
star = BigFont->GetChar('*', NULL);
|
||||
bstar = star;
|
||||
}
|
||||
}
|
||||
else // Strife needs some handling, too!
|
||||
{
|
||||
int dummywidth;
|
||||
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
|
||||
star = BigFont->GetChar('*', NULL);
|
||||
bstar = star;
|
||||
}
|
||||
|
||||
|
|
|
@ -1291,7 +1291,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE nothing, LPSTR cmdline, int n
|
|||
_CrtSetDbgFlag (_CrtSetDbgFlag(0) | _CRTDBG_LEAK_CHECK_DF);
|
||||
|
||||
// Use this to break at a specific allocation number.
|
||||
//_crtBreakAlloc = 3660;
|
||||
//_crtBreakAlloc = 30055;
|
||||
#endif
|
||||
|
||||
DoMain (hInstance);
|
||||
|
|
Loading…
Reference in a new issue