📄 gnewfont.cc
字号:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/platformFont.h"
#include "platform/profiler.h"
#include "platform/platformMutex.h"
#include "console/console.h"
#include "core/stream.h"
#include "dgl/gBitmap.h"
#include "core/fileStream.h"
#include "core/findMatch.h"
#include "dgl/gTexManager.h"
#include "dgl/gNewFont.h"
#include "util/safeDelete.h"
#include "core/frameAllocator.h"
#include "core/unicode.h"
#include "zlib.h"
S32 GFont::smSheetIdCount = 0;
const U32 GFont::csm_fileVersion = 3;
ConsoleFunction(populateFontCacheString, void, 4, 4, "(faceName, size, string) - "
"Populate the font cache for the specified font with characters from the specified string.")
{
Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
if(f.isNull())
{
Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
return;
}
if(!f->hasPlatformFont())
{
Con::errorf("populateFontCacheString - font '%s %d' has no platform font! Cannot generate more characters.", argv[1], dAtoi(argv[2]));
return;
}
// This has the side effect of generating character info, including the bitmaps.
f->getStrWidthPrecise(argv[3]);
}
ConsoleFunction(populateFontCacheRange, void, 5, 5, "(faceName, size, rangeStart, rangeEnd) - "
"Populate the font cache for the specified font with Unicode code points in the specified range. "
"Note we only support BMP-0, so code points range from 0 to 65535.")
{
Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
if(f.isNull())
{
Con::errorf("populateFontCacheRange - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
return;
}
U32 rangeStart = dAtoi(argv[3]);
U32 rangeEnd = dAtoi(argv[4]);
if(rangeStart > rangeEnd)
{
Con::errorf("populateFontCacheRange - range start is after end!");
return;
}
if(!f->hasPlatformFont())
{
Con::errorf("populateFontCacheRange - font '%s %d' has no platform font! Cannot generate more characters.", argv[1], dAtoi(argv[2]));
return;
}
// This has the side effect of generating character info, including the bitmaps.
for(U32 i=rangeStart; i<rangeEnd; i++)
{
if(f->isValidChar(i))
f->getCharWidth(i);
else
Con::warnf("populateFontCacheRange - skipping invalid char 0x%x", i);
}
// All done!
}
ConsoleFunction(dumpFontCacheStatus, void, 1, 1, "() - Return a full description "
"of all cached fonts, along with info on the codepoints each contains.")
{
FindMatch match("*.uft", 4096);
ResourceManager->findMatches(&match);
Con::printf("--------------------------------------------------------------------------");
Con::printf(" Font Cache Usage Report (%d fonts found)", match.numMatches());
for (U32 i = 0; i < match.numMatches(); i++)
{
char *curMatch = match.matchList[i];
Resource<GFont> font = ResourceManager->load(curMatch);
// Deal with inexplicably missing or failed to load fonts.
if (font.isNull())
{
Con::errorf(" o Couldn't load font : %s", curMatch);
continue;
}
// Ok, dump info!
font->dumpInfo();
}
}
ConsoleFunction(writeFontCache, void, 1, 1, "() - force all cached fonts to"
"serialize themselves to the cache.")
{
FindMatch match("*.uft", 4096);
ResourceManager->findMatches(&match);
Con::printf("--------------------------------------------------------------------------");
Con::printf(" Writing font cache to disk (%d fonts found)", match.numMatches());
for (U32 i = 0; i < match.numMatches(); i++)
{
char *curMatch = match.matchList[i];
Resource<GFont> font = ResourceManager->load(curMatch);
// Deal with inexplicably missing or failed to load fonts.
if (font.isNull())
{
Con::errorf(" o Couldn't find font : %s", curMatch);
continue;
}
// Ok, dump info!
FileStream stream;
if(ResourceManager->openFileForWrite(stream, curMatch))
{
Con::printf(" o Writing '%s' to disk...", curMatch);
font->write(stream);
stream.close();
}
else
{
Con::errorf(" o Could not open '%s' for write!", curMatch);
}
}
}
ConsoleFunction(populateAllFontCacheString, void, 2, 2, "(string) - "
"Populate the font cache for all fonts with characters from the specified string.")
{
FindMatch match("*.uft", 4096);
ResourceManager->findMatches(&match);
Con::printf("Populating font cache with string '%s' (%d fonts found)", argv[1], match.numMatches());
for (U32 i = 0; i < match.numMatches(); i++)
{
char *curMatch = match.matchList[i];
Resource<GFont> font = ResourceManager->load(curMatch);
// Deal with inexplicably missing or failed to load fonts.
if (font.isNull())
{
Con::errorf(" o Couldn't load font : %s", curMatch);
continue;
}
if(!font->hasPlatformFont())
{
Con::errorf("populateAllFontCacheString - font '%s' has no platform font! Cannot generate more characters.", curMatch);
continue;
}
// This has the side effect of generating character info, including the bitmaps.
font->getStrWidthPrecise(argv[1]);
}
}
ConsoleFunction(populateAllFontCacheRange, void, 3, 3, "(rangeStart, rangeEnd) - "
"Populate the font cache for all fonts with Unicode code points in the specified range. "
"Note we only support BMP-0, so code points range from 0 to 65535.")
{
U32 rangeStart = dAtoi(argv[1]);
U32 rangeEnd = dAtoi(argv[2]);
if(rangeStart > rangeEnd)
{
Con::errorf("populateAllFontCacheRange - range start is after end!");
return;
}
FindMatch match("*.uft", 4096);
ResourceManager->findMatches(&match);
Con::printf("Populating font cache with range 0x%x to 0x%x (%d fonts found)", rangeStart, rangeEnd, match.numMatches());
for (U32 i = 0; i < match.numMatches(); i++)
{
char *curMatch = match.matchList[i];
Resource<GFont> font = ResourceManager->load(curMatch);
// Deal with inexplicably missing or failed to load fonts.
if (font.isNull())
{
Con::errorf(" o Couldn't load font : %s", curMatch);
continue;
}
if(!font->hasPlatformFont())
{
Con::errorf("populateAllFontCacheRange - font '%s' has no platform font! Cannot generate more characters.", curMatch);
continue;
}
// This has the side effect of generating character info, including the bitmaps.
Con::printf(" o Populating font '%s'", curMatch);
for(U32 i=rangeStart; i<rangeEnd; i++)
{
if(font->isValidChar(i))
font->getCharWidth(i);
else
Con::warnf("populateAllFontCacheRange - skipping invalid char 0x%x", i);
}
}
// All done!
}
ConsoleFunction(exportCachedFont, void, 6, 6, "(fontName, size, fileName, padding, kerning) - "
"Export specified font to the specified filename as a PNG. The "
"image can then be processed in Photoshop or another tool and "
"reimported using importCachedFont. Characters in the font are"
"exported as one long strip.")
{
// Read in some params.
const char *fontName = argv[1];
S32 fontSize = dAtoi(argv[2]);
const char *fileName = argv[3];
S32 padding = dAtoi(argv[4]);
S32 kerning = dAtoi(argv[5]);
// Tell the font to export itself.
Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
if(f.isNull())
{
Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
return;
}
f->exportStrip(fileName, padding, kerning);
}
ConsoleFunction(importCachedFont, void, 6, 6, "(fontName, size, fileName, padding, kerning) - "
"Import an image strip from exportCachedFont. Call with the "
"same parameters you called exportCachedFont.")
{
// Read in some params.
const char *fontName = argv[1];
S32 fontSize = dAtoi(argv[2]);
const char *fileName = argv[3];
S32 padding = dAtoi(argv[4]);
S32 kerning = dAtoi(argv[5]);
// Tell the font to import itself.
Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
if(f.isNull())
{
Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
return;
}
f->importStrip(fileName, padding, kerning);
}
ConsoleFunction(duplicateCachedFont, void, 4, 4, "(oldFontName, oldFontSize, newFontName) -"
"Copy the specified old font to a new name. The new copy will not have a "
"platform font backing it, and so will never have characters added to it. "
"But this is useful for making copies of fonts to add postprocessing effects "
"to via exportCachedFont.")
{
char newFontFile[256];
GFont::getFontCacheFilename(argv[3], dAtoi(argv[2]), 256, newFontFile);
// Load the original font.
Resource<GFont> font = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
// Deal with inexplicably missing or failed to load fonts.
if (font.isNull())
{
Con::errorf(" o Couldn't find font : %s", newFontFile);
return;
}
// Ok, dump info!
FileStream stream;
if(ResourceManager->openFileForWrite(stream, newFontFile))
{
Con::printf(" o Writing duplicate font '%s' to disk...", newFontFile);
font->write(stream);
stream.close();
}
else
{
Con::errorf(" o Could not open '%s' for write!", newFontFile);
}
}
ResourceInstance* constructNewFont(Stream& stream)
{
GFont *ret = new GFont;
if(!ret->read(stream))
{
SAFE_DELETE(ret);
}
if(ret)
{
ret->mPlatformFont = createPlatformFont(ret->mFaceName, ret->mSize, ret->mCharSet);
}
return ret;
}
void GFont::getFontCacheFilename(const char *faceName, U32 size, U32 buffLen, char *outBuff)
{
dSprintf(outBuff, buffLen, "%s/%s %d (%s).uft", Con::getVariable("$GUI::fontCacheDirectory"), faceName, size, getCharSetName(0));
}
Resource<GFont> GFont::create(const char *faceName, U32 size, const char *cacheDirectory, U32 charset /* = TGE_ANSI_CHARSET */)
{
char buf[256];
dSprintf(buf, sizeof(buf), "%s/%s %d (%s).uft", cacheDirectory, faceName, size, getCharSetName(charset));
Resource<GFont> ret = ResourceManager->load(buf);
if(bool(ret))
{
ret->mGFTFile = StringTable->insert(buf);
return ret;
}
PlatformFont *platFont = createPlatformFont(faceName, size, charset);
if (platFont == NULL)
{
AssertISV(dStricmp(faceName, "Arial") != 0, "Error, The Arial Font must always be available!");
// Need to handle this case better. For now, let's just return a font that we're
// positive exists, in the correct size...
return create("Arial", size, cacheDirectory, charset);
}
GFont *resFont = new GFont;
resFont->mPlatformFont = platFont;
resFont->addSheet();
resFont->mGFTFile = StringTable->insert(buf);
resFont->mFaceName = StringTable->insert(faceName);
resFont->mSize = size;
resFont->mCharSet = charset;
resFont->mHeight = platFont->getFontHeight();
resFont->mBaseline = platFont->getFontBaseLine();
resFont->mAscent = platFont->getFontBaseLine();
resFont->mDescent = platFont->getFontHeight() - platFont->getFontBaseLine();
ResourceManager->add(buf, resFont, false);
return ResourceManager->load(buf);
}
//-------------------------------------------------------------------------
GFont::GFont()
{
VECTOR_SET_ASSOCIATION(mCharInfoList);
VECTOR_SET_ASSOCIATION(mTextureSheets);
for (U32 i = 0; i < (sizeof(mRemapTable) / sizeof(S32)); i++)
mRemapTable[i] = -1;
mCurX = mCurY = mCurSheet = -1;
mPlatformFont = NULL;
mGFTFile = NULL;
mFaceName = NULL;
mSize = 0;
mCharSet = 0;
mMutex = Mutex::createMutex();
}
GFont::~GFont()
{
if(mGFTFile)
{
FileStream stream;
if(ResourceManager->openFileForWrite(stream, mGFTFile))
{
write(stream);
stream.close();
}
}
S32 i;
for(i = 0;i < mCharInfoList.size();i++)
{
SAFE_DELETE_ARRAY(mCharInfoList[i].bitmapData);
}
SAFE_DELETE(mPlatformFont);
Mutex::destroyMutex(mMutex);
}
void GFont::dumpInfo()
{
// Number and extent of mapped characters?
U32 mapCount = 0, mapBegin=0xFFFF, mapEnd=0;
for(U32 i=0; i<0x10000; i++)
{
if(mRemapTable[i] != -1)
{
mapCount++;
if(i<mapBegin) mapBegin = i;
if(i>mapEnd) mapEnd = i;
}
}
// Let's write out all the info we can on this font.
Con::printf(" '%s' %dpt", mFaceName, mSize);
Con::printf(" - %d texture sheets, %d mapped characters.", mTextureSheets.size(), mapCount);
if(mapCount)
Con::printf(" - Codepoints range from 0x%x to 0x%x.", mapBegin, mapEnd);
else
Con::printf(" - No mapped codepoints.", mapBegin, mapEnd);
Con::printf(" - Platform font is %s.", (mPlatformFont ? "present" : "not present") );
}
//////////////////////////////////////////////////////////////////////////
bool GFont::loadCharInfo(const UTF16 ch)
{
if(mRemapTable[ch] != -1)
return true; // Not really an error
if(mPlatformFont && mPlatformFont->isValidChar(ch))
{
Mutex::lockMutex(mMutex); // the CharInfo returned by mPlatformFont is static data, must protect from changes.
PlatformFont::CharInfo &ci = mPlatformFont->getCharInfo(ch);
if(ci.bitmapData)
addBitmap(ci);
mCharInfoList.push_back(ci);
mRemapTable[ch] = mCharInfoList.size() - 1;
Mutex::unlockMutex(mMutex);
return true;
}
return false;
}
void GFont::addBitmap(PlatformFont::CharInfo &charInfo)
{
U32 nextCurX = U32(mCurX + charInfo.width ); /*7) & ~0x3;*/
U32 nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
// These are here for postmortem debugging.
bool routeA = false, routeB = false;
if(mCurSheet == -1 || nextCurY >= TextureSheetSize)
{
routeA = true;
addSheet();
// Recalc our nexts.
nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
}
if( nextCurX >= TextureSheetSize)
{
routeB = true;
mCurX = 0;
mCurY = nextCurY;
// Recalc our nexts.
nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -