mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-06-01 12:16:07 -06:00
Read json fonts
A font.json maps characters to image file names. The Font class caches all the glyph images then draws the appropriate one(s) at blt time.
This commit is contained in:
parent
97a84b8e27
commit
4896fbe384
@ -3,9 +3,9 @@
|
|||||||
[General]
|
[General]
|
||||||
|
|
||||||
[Fonts]
|
[Fonts]
|
||||||
kifntDefault=standardfont.fnt
|
kifntDefault=standardfont.json
|
||||||
kifntShadow=shadowfont.fnt
|
kifntShadow=shadowfont.json
|
||||||
kifntButton=buttonfont.fnt
|
kifntButton=buttonfont.json
|
||||||
kifntTitle=titlefont.fnt
|
kifntTitle=titlefont.json
|
||||||
kifntEcom=standardfont.fnt
|
kifntEcom=standardfont.json
|
||||||
kifntHud=hudfont.fnt
|
kifntHud=hudfont.json
|
||||||
|
|||||||
259
game/font.cpp
259
game/font.cpp
@ -1,9 +1,9 @@
|
|||||||
#include "ht.h"
|
#include "ht.h"
|
||||||
|
#include "yajl/wrapper/jsontypes.h"
|
||||||
|
#include "yajl/wrapper/jsonbuilder.h"
|
||||||
|
|
||||||
namespace wi {
|
namespace wi {
|
||||||
|
|
||||||
#define kcchFont 256
|
|
||||||
|
|
||||||
Font *LoadFont(char *pszFont)
|
Font *LoadFont(char *pszFont)
|
||||||
{
|
{
|
||||||
Font *pfnt = new Font;
|
Font *pfnt = new Font;
|
||||||
@ -19,83 +19,107 @@ Font *LoadFont(char *pszFont)
|
|||||||
|
|
||||||
Font::Font()
|
Font::Font()
|
||||||
{
|
{
|
||||||
m_pfnth = NULL;
|
m_nGlyphOverlap = 0;
|
||||||
m_mpchpbCodeEven = NULL;
|
m_nLineOverlap = 0;
|
||||||
m_mpchpbCodeOdd = NULL;
|
|
||||||
m_nGlyphOverlap = 0;
|
|
||||||
m_nLineOverlap = 0;
|
|
||||||
m_cxEllipsis = 0;
|
m_cxEllipsis = 0;
|
||||||
|
m_cy = 0;
|
||||||
|
m_ptbmDefault = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font()
|
Font::~Font()
|
||||||
{
|
{
|
||||||
if (m_pfnth != NULL)
|
delete m_ptbmDefault;
|
||||||
gpakr.UnmapFile(&m_fmap);
|
|
||||||
|
|
||||||
for (int ch = 0; ch < kcchFont; ch++) {
|
|
||||||
if (m_mpchpbCodeEven != NULL) {
|
|
||||||
if (m_mpchpbCodeEven[ch] != NULL)
|
|
||||||
gmmgr.FreePtr(m_mpchpbCodeEven[ch]);
|
|
||||||
}
|
|
||||||
if (m_mpchpbCodeOdd != NULL) {
|
|
||||||
if (m_mpchpbCodeOdd[ch] != NULL)
|
|
||||||
gmmgr.FreePtr(m_mpchpbCodeOdd[ch]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_mpchpbCodeEven != NULL)
|
|
||||||
gmmgr.FreePtr(m_mpchpbCodeEven);
|
|
||||||
if (m_mpchpbCodeOdd != NULL)
|
|
||||||
gmmgr.FreePtr(m_mpchpbCodeOdd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Font::Load(char *pszFont)
|
bool Font::Load(char *pszFont)
|
||||||
{
|
{
|
||||||
m_pfnth = (FontHeader *)gpakr.MapFile(pszFont, &m_fmap);
|
// Read json
|
||||||
if (m_pfnth == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
byte *apbT[kcchFont];
|
FileMap fmap;
|
||||||
memset(apbT, 0, sizeof(apbT));
|
char *pszJson = (char *)gpakr.MapFile(pszFont, &fmap);
|
||||||
|
|
||||||
m_mpchpbCodeEven = (byte **)gmmgr.AllocPtr(sizeof(byte *) * kcchFont);
|
// Parse json
|
||||||
if (m_mpchpbCodeEven == NULL)
|
|
||||||
return false;
|
|
||||||
gmmgr.WritePtr(m_mpchpbCodeEven, 0, apbT, sizeof(apbT));
|
|
||||||
|
|
||||||
m_mpchpbCodeOdd = (byte **)gmmgr.AllocPtr(sizeof(byte *) * kcchFont);
|
json::JsonBuilder builder;
|
||||||
if (m_mpchpbCodeOdd == NULL)
|
builder.Start();
|
||||||
return false;
|
if (!builder.Update(pszJson, (int)strlen(pszJson))) {
|
||||||
gmmgr.WritePtr(m_mpchpbCodeOdd, 0, apbT, sizeof(apbT));
|
gpakr.UnmapFile(&fmap);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
json::JsonObject *obj = builder.End();
|
||||||
|
if (obj == NULL) {
|
||||||
|
gpakr.UnmapFile(&fmap);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gpakr.UnmapFile(&fmap);
|
||||||
|
|
||||||
m_cxEllipsis = GetTextExtent("...");
|
// Font json map
|
||||||
|
|
||||||
return true;
|
json::JsonMap *map = (json::JsonMap *)obj;
|
||||||
|
|
||||||
|
// Map of characters to glyph image filenames
|
||||||
|
|
||||||
|
json::JsonMap *glyphMap = (json::JsonMap *)map->GetObject("glyph_map");
|
||||||
|
if (glyphMap == NULL) {
|
||||||
|
delete map;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all the characters and cache corresponding TImages via std::map
|
||||||
|
|
||||||
|
Enum enm;
|
||||||
|
const char *key;
|
||||||
|
while ((key = glyphMap->EnumKeys(&enm)) != NULL) {
|
||||||
|
|
||||||
|
json::JsonString *glyph = (json::JsonString *)glyphMap->GetObject(key);
|
||||||
|
if (glyph == NULL) {
|
||||||
|
delete map;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char szGlyphFn[64];
|
||||||
|
strncpyz(szGlyphFn, glyph->GetString(), sizeof(szGlyphFn));
|
||||||
|
|
||||||
|
if (!m_map.insert(std::make_pair(std::string(key), CreateTBitmap(szGlyphFn))).second) {
|
||||||
|
LOG() << "Error adding \"" << key << "\" to font map";
|
||||||
|
delete map;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default character, for when requested character isn't mapped
|
||||||
|
|
||||||
|
char sz[64];
|
||||||
|
strncpyz(sz, map->GetString("default"), sizeof(sz));
|
||||||
|
m_ptbmDefault = CreateTBitmap(sz);
|
||||||
|
if (m_ptbmDefault == NULL) {
|
||||||
|
delete map;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cy = map->GetInteger("height");
|
||||||
|
m_nGlyphOverlap = map->GetInteger("glyph_overlap");
|
||||||
|
m_nLineOverlap = map->GetInteger("line_overlap");
|
||||||
|
|
||||||
|
if (TBitmapExists('.'))
|
||||||
|
m_cxEllipsis = GetTextExtent("...");
|
||||||
|
|
||||||
|
delete map;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::GetTextExtent(const char *psz)
|
int Font::GetTextExtent(const char *psz)
|
||||||
{
|
{
|
||||||
int cx = 0;
|
return GetTextExtent(psz, (int)strlen(psz));
|
||||||
while (*psz != 0) {
|
|
||||||
byte ch = *psz++;
|
|
||||||
cx += m_pfnth->acxChar[ch] - m_nGlyphOverlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shadow allows 1 pixel overlap but the last char doesn't overlap
|
|
||||||
|
|
||||||
return cx + m_nGlyphOverlap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::GetTextExtent(const char *psz, int cch)
|
int Font::GetTextExtent(const char *psz, int cch)
|
||||||
{
|
{
|
||||||
int cx = 0;
|
int width = 0;
|
||||||
while (cch-- > 0) {
|
for (int i = 0; i < cch; i++) {
|
||||||
byte ch = *psz++;
|
width += GetTBitmap(psz[i])->Width() - m_nGlyphOverlap;
|
||||||
cx += m_pfnth->acxChar[ch] - m_nGlyphOverlap;
|
|
||||||
}
|
}
|
||||||
|
return width;
|
||||||
// Shadow allows 1 pixel overlap but the last char doesn't overlap
|
|
||||||
|
|
||||||
return cx + m_nGlyphOverlap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::CalcMultilineHeight(char *psz, int cxMultiline)
|
int Font::CalcMultilineHeight(char *psz, int cxMultiline)
|
||||||
@ -105,7 +129,7 @@ int Font::CalcMultilineHeight(char *psz, int cxMultiline)
|
|||||||
while (pszNext != NULL) {
|
while (pszNext != NULL) {
|
||||||
char *pszStart = pszNext;
|
char *pszStart = pszNext;
|
||||||
CalcBreak(cxMultiline, &pszNext);
|
CalcBreak(cxMultiline, &pszNext);
|
||||||
cy += BigWord(m_pfnth->cy) - m_nLineOverlap;
|
cy += m_cy - m_nLineOverlap;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cy;
|
return cy;
|
||||||
@ -114,7 +138,7 @@ int Font::CalcMultilineHeight(char *psz, int cxMultiline)
|
|||||||
void Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx, int cy,
|
void Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx, int cy,
|
||||||
bool fEllipsis)
|
bool fEllipsis)
|
||||||
{
|
{
|
||||||
int cyFont = BigWord(m_pfnth->cy) - m_nLineOverlap;
|
int cyFont = m_cy - m_nLineOverlap;
|
||||||
int cyT = cyFont;
|
int cyT = cyFont;
|
||||||
char *pszNext = psz;
|
char *pszNext = psz;
|
||||||
while (pszNext != NULL) {
|
while (pszNext != NULL) {
|
||||||
@ -191,7 +215,7 @@ char *Font::FindNextNonBreakingChar(char *psz)
|
|||||||
int Font::CalcBreak(int cx, char **ppsz, bool fChop)
|
int Font::CalcBreak(int cx, char **ppsz, bool fChop)
|
||||||
{
|
{
|
||||||
char *pchBreakAfter = NULL;
|
char *pchBreakAfter = NULL;
|
||||||
int cchBreak;
|
int cchBreak = 0;
|
||||||
int cxT = 0;
|
int cxT = 0;
|
||||||
char *pchT = *ppsz;
|
char *pchT = *ppsz;
|
||||||
int cch = 0;
|
int cch = 0;
|
||||||
@ -228,7 +252,7 @@ int Font::CalcBreak(int cx, char **ppsz, bool fChop)
|
|||||||
|
|
||||||
// At right edge yet?
|
// At right edge yet?
|
||||||
|
|
||||||
int cxChar = m_pfnth->acxChar[(byte)*pchT] - m_nGlyphOverlap;
|
int cxChar = GetTBitmap(pchT[0])->Width() - m_nGlyphOverlap;
|
||||||
if (cxT + cxChar > cx) {
|
if (cxT + cxChar > cx) {
|
||||||
// If last break exists, use it. Return pointer skips past break char
|
// If last break exists, use it. Return pointer skips past break char
|
||||||
|
|
||||||
@ -286,103 +310,38 @@ int Font::CalcBreak(int cx, char **ppsz, bool fChop)
|
|||||||
|
|
||||||
int Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch, dword *mpscaiclr)
|
int Font::DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch, dword *mpscaiclr)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
if (cch == -1)
|
||||||
for (int ichT = 0; ichT < cch; ichT++)
|
|
||||||
Assert((word)psz[ichT] < (word)kcchFont);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (cch == -1)
|
|
||||||
cch = (int)strlen(psz);
|
cch = (int)strlen(psz);
|
||||||
|
|
||||||
// Clip entire line of text first.
|
int pos = 0;
|
||||||
|
for (int i = 0; i < cch; i++) {
|
||||||
|
|
||||||
Size siz;
|
// Draw the character
|
||||||
pbm->GetSize(&siz);
|
// If character unavailable, GetTBitmap() will return the default character
|
||||||
|
|
||||||
// Top clip
|
GetTBitmap(psz[i])->BltTo(pbm, x + pos, y);
|
||||||
|
pos = pos + GetTBitmap(psz[i])->Width() - m_nGlyphOverlap;
|
||||||
|
}
|
||||||
|
|
||||||
if (y < 0)
|
return 0;
|
||||||
return 0;
|
}
|
||||||
if (y + BigWord(m_pfnth->cy) > siz.cy)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Don't include x clipped chars
|
TBitmap *Font::GetTBitmap(char sz)
|
||||||
// Left clip
|
{
|
||||||
|
std::string str(1, sz);
|
||||||
|
FontMap::iterator it = m_map.find(str);
|
||||||
|
if (it == m_map.end()) {
|
||||||
|
// LOG() << "Missing character \"" << str << "\"";
|
||||||
|
return m_ptbmDefault;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
int ich = 0;
|
bool Font::TBitmapExists(char sz)
|
||||||
int xT = x;
|
{
|
||||||
char *pszT;
|
std::string str(1, sz);
|
||||||
for (pszT = psz; pszT - psz < cch; pszT++) {
|
FontMap::iterator it = m_map.find(str);
|
||||||
if (xT < 0) {
|
return it != m_map.end();
|
||||||
ich++;
|
|
||||||
xT += m_pfnth->acxChar[(byte)*pszT] - m_nGlyphOverlap;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int xDst = xT;
|
|
||||||
|
|
||||||
// Right clip
|
|
||||||
|
|
||||||
int cchT = 0;
|
|
||||||
for (; pszT - psz < cch; pszT++) {
|
|
||||||
int cx = m_pfnth->acxChar[(byte)*pszT] - m_nGlyphOverlap;
|
|
||||||
if (xT + cx <= siz.cx) {
|
|
||||||
cchT++;
|
|
||||||
xT += cx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cchT == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cch = cchT;
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
|
|
||||||
xT = xDst;
|
|
||||||
byte *pbDst = pbm->GetBits() + (long)y * siz.cx + xT;
|
|
||||||
char *pszMax = &psz[ich + cch];
|
|
||||||
|
|
||||||
for (pszT = &psz[ich]; pszT < pszMax; pszT++) {
|
|
||||||
char ch = *pszT;
|
|
||||||
int cxChar = m_pfnth->acxChar[(byte)ch];
|
|
||||||
if (cxChar == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
byte *pbDraw;
|
|
||||||
if (xT & 1) {
|
|
||||||
pbDraw = m_mpchpbCodeOdd[(byte)ch];
|
|
||||||
} else {
|
|
||||||
pbDraw = m_mpchpbCodeEven[(byte)ch];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pbDraw == NULL) {
|
|
||||||
ScanData *psd = (ScanData *)(((byte *)m_pfnth) +
|
|
||||||
BigWord(m_pfnth->mpchibsd[(byte)*pszT]));
|
|
||||||
word cb = Compile8Thunk(gpbScratch, psd, xT & 1);
|
|
||||||
byte *pbT = (byte *)gmmgr.AllocPtr(cb);
|
|
||||||
if (pbT != NULL) {
|
|
||||||
gmmgr.WritePtr(pbT, 0, gpbScratch, cb);
|
|
||||||
if (xT & 1) {
|
|
||||||
gmmgr.WritePtr(m_mpchpbCodeOdd, (byte)ch * sizeof(byte *),
|
|
||||||
&pbT, sizeof(byte *));
|
|
||||||
} else {
|
|
||||||
gmmgr.WritePtr(m_mpchpbCodeEven, (byte)ch * sizeof(byte *),
|
|
||||||
&pbT, sizeof(byte *));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pbDraw = gpbScratch;
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawDispatchThunk(pbDraw, NULL, pbDst, siz.cx - cxChar, mpscaiclr,
|
|
||||||
gmpiclriclrShadow);
|
|
||||||
pbDst += cxChar - m_nGlyphOverlap;
|
|
||||||
xT += cxChar - m_nGlyphOverlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
return xT - xDst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace wi
|
} // namespace wi
|
||||||
|
|||||||
@ -404,13 +404,6 @@ bool Game::Init(int imm)
|
|||||||
Assert("LoadFont failed");
|
Assert("LoadFont failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: shadow font glyphs need to overlap by one pixel when drawn
|
|
||||||
|
|
||||||
if (strcmp(szT, "shadowfont.fnt") == 0) {
|
|
||||||
gapfnt[ifnt]->SetGlyphOverlap(1);
|
|
||||||
gapfnt[ifnt]->SetLineOverlap(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_wf |= kfGameInitFonts;
|
m_wf |= kfGameInitFonts;
|
||||||
|
|||||||
52
game/ht.h
52
game/ht.h
@ -1181,32 +1181,21 @@ private:
|
|||||||
DibBitmap **m_pbmAtlases;
|
DibBitmap **m_pbmAtlases;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FontHeader // fnth
|
// Font
|
||||||
{
|
|
||||||
word cy;
|
#include <map>
|
||||||
byte acxChar[256];
|
typedef std::map<std::string, TBitmap *> FontMap;
|
||||||
word mpchibsd[256];
|
|
||||||
ScanData asd[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
class Font // fnt
|
class Font // fnt
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Font() secFont;
|
Font();
|
||||||
~Font() secFont;
|
~Font();
|
||||||
|
|
||||||
bool Load(char *pszFont) secFont;
|
bool Load(char *pszFont);
|
||||||
|
|
||||||
int GetHeight() {
|
int GetHeight() {
|
||||||
return BigWord(m_pfnth->cy);
|
return m_cy;
|
||||||
}
|
|
||||||
|
|
||||||
void SetGlyphOverlap(int nGlyphOverlap) {
|
|
||||||
m_nGlyphOverlap = nGlyphOverlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLineOverlap(int nLineOverlap) {
|
|
||||||
m_nLineOverlap = nLineOverlap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetGlyphOverlap() {
|
int GetGlyphOverlap() {
|
||||||
@ -1217,29 +1206,30 @@ public:
|
|||||||
return m_nLineOverlap;
|
return m_nLineOverlap;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetTextExtent(const char *psz) secFont;
|
int GetTextExtent(const char *psz);
|
||||||
int GetTextExtent(const char *psz, int cch) secFont;
|
int GetTextExtent(const char *psz, int cch);
|
||||||
int DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch = -1,
|
int DrawText(DibBitmap *pbm, char *psz, int x, int y, int cch = -1,
|
||||||
dword *mpscaiclr = NULL) secFont;
|
Color *pclr = NULL);
|
||||||
void DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx,
|
void DrawText(DibBitmap *pbm, char *psz, int x, int y, int cx,
|
||||||
int cy, bool fEllipsis = false) secFont;
|
int cy, bool fEllipsis = false);
|
||||||
void DrawTextWithEllipsis(DibBitmap *pbm, char *psz, int cch,
|
void DrawTextWithEllipsis(DibBitmap *pbm, char *psz, int cch,
|
||||||
int x, int y, int cx, bool fForce = false);
|
int x, int y, int cx, bool fForce = false);
|
||||||
int CalcMultilineHeight(char *psz, int cxMultiline) secFont;
|
int CalcMultilineHeight(char *psz, int cxMultiline);
|
||||||
int CalcBreak(int cx, char **psz, bool fChop = true) secFont;
|
int CalcBreak(int cx, char **psz, bool fChop = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char *FindNextNonBreakingChar(char *psz) secFont;
|
char *FindNextNonBreakingChar(char *psz);
|
||||||
|
TBitmap *GetTBitmap(char sz);
|
||||||
|
bool TBitmapExists(char sz);
|
||||||
|
|
||||||
FileMap m_fmap;
|
|
||||||
FontHeader *m_pfnth;
|
|
||||||
byte **m_mpchpbCodeEven;
|
|
||||||
byte **m_mpchpbCodeOdd;
|
|
||||||
int m_nGlyphOverlap;
|
int m_nGlyphOverlap;
|
||||||
int m_nLineOverlap;
|
int m_nLineOverlap;
|
||||||
int m_cxEllipsis;
|
int m_cxEllipsis;
|
||||||
|
int m_cy;
|
||||||
|
FontMap m_map;
|
||||||
|
TBitmap *m_ptbmDefault;
|
||||||
};
|
};
|
||||||
Font *LoadFont(char *pszFont) secFont;
|
Font *LoadFont(char *pszFont);
|
||||||
|
|
||||||
struct TileSetHeader // tseth
|
struct TileSetHeader // tseth
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user