Initial commit
This commit is contained in:
475
Torque/SDK/engine/dgl/gFont.cc
Normal file
475
Torque/SDK/engine/dgl/gFont.cc
Normal file
@@ -0,0 +1,475 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/stream.h"
|
||||
#include "dgl/gFont.h"
|
||||
#include "dgl/gBitmap.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
|
||||
S32 GOldFont::smSheetIdCount = 0;
|
||||
|
||||
ResourceInstance* constructFont(Stream& stream)
|
||||
{
|
||||
GOldFont *ret = new GOldFont;
|
||||
if(!ret->read(stream))
|
||||
{
|
||||
delete ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
const U32 GOldFont::csm_fileVersion = 1;
|
||||
|
||||
Resource<GOldFont> GOldFont::create(const char *faceName, U32 size, const char *cacheDirectory, U32 charset /* = 0 */)
|
||||
{
|
||||
char buf[256];
|
||||
dSprintf(buf, sizeof(buf), "%s/%s_%d.gft", cacheDirectory, faceName, size);
|
||||
|
||||
Resource<GOldFont> ret = ResourceManager->load(buf);
|
||||
if(bool(ret))
|
||||
return ret;
|
||||
|
||||
GOldFont *resFont = createFont(faceName, size, charset);
|
||||
if (resFont == 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);
|
||||
}
|
||||
|
||||
FileStream stream;
|
||||
if(ResourceManager->openFileForWrite(stream, buf))
|
||||
{
|
||||
resFont->write(stream);
|
||||
stream.close();
|
||||
}
|
||||
ResourceManager->add(buf, resFont, false);
|
||||
return ResourceManager->load(buf);
|
||||
}
|
||||
|
||||
GOldFont::GOldFont()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mCharInfoList);
|
||||
|
||||
for (U32 i = 0; i < 256; i++)
|
||||
mRemapTable[i] = -1;
|
||||
|
||||
mTextureSheets = NULL;
|
||||
}
|
||||
|
||||
GOldFont::~GOldFont()
|
||||
{
|
||||
delete [] mTextureSheets;
|
||||
mTextureSheets = NULL;
|
||||
}
|
||||
|
||||
void GOldFont::insertBitmap(U16 index, U8 *src, U32 stride, U32 width, U32 height, S32 xOrigin, S32 yOrigin, S32 xIncrement)
|
||||
{
|
||||
CharInfo c;
|
||||
c.bitmapIndex = -1;
|
||||
c.xOffset = 0;
|
||||
c.yOffset = 0;
|
||||
c.width = width;
|
||||
c.height = height;
|
||||
|
||||
c.xOrigin = xOrigin;
|
||||
c.yOrigin = yOrigin;
|
||||
c.xIncrement = xIncrement;
|
||||
|
||||
c.bitmapData = new U8[c.width * c.height];
|
||||
|
||||
for(U32 y = 0; S32(y) < c.height; y++)
|
||||
{
|
||||
U32 x;
|
||||
for(x = 0; x < width; x++)
|
||||
c.bitmapData[y * c.width + x] = src[y * stride + x];
|
||||
}
|
||||
mRemapTable[index] = mCharInfoList.size();
|
||||
mCharInfoList.push_back(c);
|
||||
}
|
||||
|
||||
static S32 QSORT_CALLBACK CharInfoCompare(const void *a, const void *b)
|
||||
{
|
||||
S32 ha = (*((GOldFont::CharInfo **) a))->height;
|
||||
S32 hb = (*((GOldFont::CharInfo **) b))->height;
|
||||
|
||||
return hb - ha;
|
||||
}
|
||||
|
||||
void GOldFont::pack(U32 inFontHeight, U32 inBaseLine)
|
||||
{
|
||||
mFontHeight = inFontHeight;
|
||||
mBaseLine = inBaseLine;
|
||||
|
||||
// pack all the bitmap data into sheets.
|
||||
Vector<CharInfo *> vec;
|
||||
|
||||
U32 size = mCharInfoList.size();
|
||||
U32 i;
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
CharInfo *ch = &mCharInfoList[i];
|
||||
vec.push_back(ch);
|
||||
}
|
||||
|
||||
dQsort(vec.address(), size, sizeof(CharInfo *), CharInfoCompare);
|
||||
// sorted by height
|
||||
|
||||
Vector<Point2I> sheetSizes;
|
||||
Point2I curSheetSize(256, 256);
|
||||
|
||||
S32 curY = 0;
|
||||
S32 curX = 0;
|
||||
S32 curLnHeight = 0;
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
CharInfo *ci = vec[i];
|
||||
|
||||
if(curX + ci->width > curSheetSize.x)
|
||||
{
|
||||
curY += curLnHeight;
|
||||
curX = 0;
|
||||
curLnHeight = 0;
|
||||
}
|
||||
if(curY + ci->height > curSheetSize.y)
|
||||
{
|
||||
sheetSizes.push_back(curSheetSize);
|
||||
curX = 0;
|
||||
curY = 0;
|
||||
curLnHeight = 0;
|
||||
}
|
||||
if(ci->height > curLnHeight)
|
||||
curLnHeight = ci->height;
|
||||
ci->bitmapIndex = sheetSizes.size();
|
||||
ci->xOffset = curX;
|
||||
ci->yOffset = curY;
|
||||
curX += ci->width;
|
||||
}
|
||||
curY += curLnHeight;
|
||||
|
||||
if(curY < 64)
|
||||
curSheetSize.y = 64;
|
||||
else if(curY < 128)
|
||||
curSheetSize.y = 128;
|
||||
|
||||
sheetSizes.push_back(curSheetSize);
|
||||
|
||||
Vector<GBitmap *> bitmapArray;
|
||||
|
||||
mNumSheets = sheetSizes.size();
|
||||
mTextureSheets = new TextureHandle[mNumSheets];
|
||||
|
||||
for(i = 0; i < mNumSheets; i++)
|
||||
bitmapArray.push_back(new GBitmap(sheetSizes[i].x, sheetSizes[i].y, false, GBitmap::Alpha));
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
CharInfo *ci = vec[i];
|
||||
GBitmap *bmp = bitmapArray[ci->bitmapIndex];
|
||||
S32 x, y;
|
||||
for(y = 0; y < ci->height; y++)
|
||||
for(x = 0; x < ci->width; x++)
|
||||
*bmp->getAddress(x + ci->xOffset, y + ci->yOffset) =
|
||||
ci->bitmapData[y * ci->width + x];
|
||||
delete[] ci->bitmapData;
|
||||
}
|
||||
for(i = 0; i < mNumSheets; i++)
|
||||
{
|
||||
assignSheet(i, bitmapArray[i]);
|
||||
mTextureSheets[i].setFilterNearest();
|
||||
}
|
||||
}
|
||||
|
||||
TextureHandle GOldFont::getTextureHandle(S32 index)
|
||||
{
|
||||
return mTextureSheets[index];
|
||||
}
|
||||
|
||||
void GOldFont::assignSheet(S32 sheetNum, GBitmap *bmp)
|
||||
{
|
||||
char buf[30];
|
||||
dSprintf(buf, sizeof(buf), "font_%d", smSheetIdCount++);
|
||||
mTextureSheets[sheetNum] = TextureHandle(buf, bmp);
|
||||
}
|
||||
|
||||
U32 GOldFont::getStrWidth(const char* in_pString) const
|
||||
{
|
||||
AssertFatal(in_pString != NULL, "GOldFont::getStrWidth: String is NULL, height is undefined");
|
||||
// If we ain't running debug...
|
||||
if (in_pString == NULL)
|
||||
return 0;
|
||||
|
||||
return getStrNWidth(in_pString, dStrlen(in_pString));
|
||||
}
|
||||
|
||||
U32 GOldFont::getStrWidthPrecise(const char* in_pString) const
|
||||
{
|
||||
AssertFatal(in_pString != NULL, "GOldFont::getStrWidth: String is NULL, height is undefined");
|
||||
// If we ain't running debug...
|
||||
if (in_pString == NULL)
|
||||
return 0;
|
||||
|
||||
return getStrNWidthPrecise(in_pString, dStrlen(in_pString));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
U32 GOldFont::getStrNWidth(const char *str, U32 n) const
|
||||
{
|
||||
AssertFatal(str != NULL, "GOldFont::getStrNWidth: String is NULL");
|
||||
|
||||
if (str == NULL)
|
||||
return(0);
|
||||
|
||||
U32 totWidth = 0;
|
||||
const char *curChar;
|
||||
const char *endStr;
|
||||
for (curChar = str, endStr = str + n; curChar < endStr; curChar++)
|
||||
{
|
||||
if(isValidChar(*curChar))
|
||||
{
|
||||
const CharInfo& rChar = getCharInfo(*curChar);
|
||||
totWidth += rChar.xIncrement;
|
||||
}
|
||||
else if (*curChar == '\t')
|
||||
{
|
||||
const CharInfo& rChar = getCharInfo(' ');
|
||||
totWidth += rChar.xIncrement * TabWidthInSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
return(totWidth);
|
||||
}
|
||||
|
||||
U32 GOldFont::getStrNWidthPrecise(const char *str, U32 n) const
|
||||
{
|
||||
AssertFatal(str != NULL, "GOldFont::getStrNWidth: String is NULL");
|
||||
|
||||
if (str == NULL)
|
||||
return(0);
|
||||
|
||||
U32 totWidth = 0;
|
||||
const char *curChar;
|
||||
const char *endStr;
|
||||
for (curChar = str, endStr = str + n; curChar < endStr; curChar++)
|
||||
{
|
||||
if(isValidChar(*curChar))
|
||||
{
|
||||
const CharInfo& rChar = getCharInfo(*curChar);
|
||||
totWidth += rChar.xIncrement;
|
||||
}
|
||||
else if (*curChar == '\t')
|
||||
{
|
||||
const CharInfo& rChar = getCharInfo(' ');
|
||||
totWidth += rChar.xIncrement * TabWidthInSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
// Need to check the last char to see if it has some slop...
|
||||
char endChar = str[n-1];
|
||||
if (isValidChar(endChar))
|
||||
{
|
||||
const CharInfo& rChar = getCharInfo(endChar);
|
||||
if (rChar.width > rChar.xIncrement)
|
||||
totWidth += (rChar.width - rChar.xIncrement);
|
||||
}
|
||||
}
|
||||
|
||||
return(totWidth);
|
||||
}
|
||||
|
||||
U32 GOldFont::getBreakPos(const char *string, U32 slen, U32 width, bool breakOnWhitespace)
|
||||
{
|
||||
U32 ret = 0;
|
||||
U32 lastws = 0;
|
||||
while(ret < slen)
|
||||
{
|
||||
char c = string[ret];
|
||||
if(c == '\t')
|
||||
c = ' ';
|
||||
if(!isValidChar(c))
|
||||
{
|
||||
ret++;
|
||||
continue;
|
||||
}
|
||||
if(c == ' ')
|
||||
lastws = ret+1;
|
||||
const CharInfo& rChar = getCharInfo(c);
|
||||
if(rChar.width > width || rChar.xIncrement > width)
|
||||
{
|
||||
if(lastws && breakOnWhitespace)
|
||||
return lastws;
|
||||
return ret;
|
||||
}
|
||||
width -= rChar.xIncrement;
|
||||
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GOldFont::wrapString(const char *txt, U32 lineWidth, Vector<U32> &startLineOffset, Vector<U32> &lineLen)
|
||||
{
|
||||
startLineOffset.clear();
|
||||
lineLen.clear();
|
||||
|
||||
if (!txt || !txt[0] || lineWidth < getCharWidth('W')) //make sure the line width is greater then a single character
|
||||
return;
|
||||
|
||||
U32 len = dStrlen(txt);
|
||||
|
||||
U32 startLine;
|
||||
|
||||
for (U32 i = 0; i < len;)
|
||||
{
|
||||
startLine = i;
|
||||
startLineOffset.push_back(startLine);
|
||||
|
||||
// loop until the string is too large
|
||||
bool needsNewLine = false;
|
||||
U32 lineStrWidth = 0;
|
||||
for (; i < len; i++)
|
||||
{
|
||||
if(isValidChar(txt[i]))
|
||||
{
|
||||
lineStrWidth += getCharInfo(txt[i]).xIncrement;
|
||||
if ( txt[i] == '\n' || lineStrWidth > lineWidth )
|
||||
{
|
||||
needsNewLine = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsNewLine)
|
||||
{
|
||||
// we are done!
|
||||
lineLen.push_back(i - startLine);
|
||||
return;
|
||||
}
|
||||
|
||||
// now determine where to put the newline
|
||||
// else we need to backtrack until we find a either space character
|
||||
// or \\ character to break up the line.
|
||||
S32 j;
|
||||
for (j = i - 1; j >= startLine; j--)
|
||||
{
|
||||
if (dIsspace(txt[j]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (j < startLine)
|
||||
{
|
||||
// the line consists of a single word!
|
||||
// So, just break up the word
|
||||
j = i - 1;
|
||||
}
|
||||
lineLen.push_back(j - startLine);
|
||||
i = j;
|
||||
|
||||
// now we need to increment through any space characters at the
|
||||
// beginning of the next line
|
||||
for (i++; i < len; i++)
|
||||
{
|
||||
if (!dIsspace(txt[i]) || txt[i] == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Persist functionality
|
||||
//
|
||||
static const U32 csm_fileVersion = 1;
|
||||
|
||||
bool GOldFont::read(Stream& io_rStream)
|
||||
{
|
||||
// Handle versioning
|
||||
U32 version;
|
||||
io_rStream.read(&version);
|
||||
if(version != csm_fileVersion)
|
||||
return false;
|
||||
|
||||
// Read Font Information
|
||||
io_rStream.read(&mFontHeight);
|
||||
io_rStream.read(&mBaseLine);
|
||||
|
||||
U32 size = 0;
|
||||
io_rStream.read(&size);
|
||||
mCharInfoList.setSize(size);
|
||||
U32 i;
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
CharInfo *ci = &mCharInfoList[i];
|
||||
io_rStream.read(&ci->bitmapIndex);
|
||||
io_rStream.read(&ci->xOffset);
|
||||
io_rStream.read(&ci->yOffset);
|
||||
io_rStream.read(&ci->width);
|
||||
io_rStream.read(&ci->height);
|
||||
io_rStream.read(&ci->xOrigin);
|
||||
io_rStream.read(&ci->yOrigin);
|
||||
io_rStream.read(&ci->xIncrement);
|
||||
}
|
||||
io_rStream.read(&mNumSheets);
|
||||
|
||||
mTextureSheets = new TextureHandle[mNumSheets];
|
||||
for(i = 0; i < mNumSheets; i++)
|
||||
{
|
||||
GBitmap *bmp = new GBitmap;
|
||||
if(!bmp->readPNG(io_rStream))
|
||||
{
|
||||
delete bmp;
|
||||
return false;
|
||||
}
|
||||
assignSheet(i, bmp);
|
||||
mTextureSheets[i].setFilterNearest();
|
||||
}
|
||||
|
||||
// Read character remap table
|
||||
for(i = 0; i < 256; i++)
|
||||
io_rStream.read(&mRemapTable[i]);
|
||||
|
||||
return (io_rStream.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
||||
bool
|
||||
GOldFont::write(Stream& stream) const
|
||||
{
|
||||
// Handle versioning
|
||||
stream.write(csm_fileVersion);
|
||||
|
||||
// Write Font Information
|
||||
stream.write(mFontHeight);
|
||||
stream.write(mBaseLine);
|
||||
|
||||
stream.write(U32(mCharInfoList.size()));
|
||||
U32 i;
|
||||
for(i = 0; i < mCharInfoList.size(); i++)
|
||||
{
|
||||
const CharInfo *ci = &mCharInfoList[i];
|
||||
stream.write(ci->bitmapIndex);
|
||||
stream.write(ci->xOffset);
|
||||
stream.write(ci->yOffset);
|
||||
stream.write(ci->width);
|
||||
stream.write(ci->height);
|
||||
stream.write(ci->xOrigin);
|
||||
stream.write(ci->yOrigin);
|
||||
stream.write(ci->xIncrement);
|
||||
}
|
||||
stream.write(mNumSheets);
|
||||
for(i = 0; i < mNumSheets; i++)
|
||||
mTextureSheets[i].getBitmap()->writePNG(stream);
|
||||
|
||||
for(i = 0; i < 256; i++)
|
||||
stream.write(mRemapTable[i]);
|
||||
|
||||
return (stream.getStatus() == Stream::Ok);
|
||||
}
|
||||
Reference in New Issue
Block a user