📄 gnewfont.cc
字号:
nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
}
// Check the Y once more - sometimes we advance to a new row and run off
// the end.
if(nextCurY >= TextureSheetSize)
{
routeA = true;
addSheet();
// Recalc our nexts.
nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
}
charInfo.bitmapIndex = mCurSheet;
charInfo.xOffset = mCurX;
charInfo.yOffset = mCurY;
mCurX = nextCurX;
S32 x, y;
GBitmap *bmp = mTextureSheets[mCurSheet].getBitmap();
AssertFatal(bmp->getFormat() == GBitmap::Alpha, "GFont::addBitmap - cannot added characters to non-greyscale textures!");
for(y = 0;y < charInfo.height;y++)
for(x = 0;x < charInfo.width;x++)
*bmp->getAddress(x + charInfo.xOffset, y + charInfo.yOffset) = charInfo.bitmapData[y * charInfo.width + x];
mTextureSheets[mCurSheet].refresh();
}
void GFont::addSheet()
{
char buf[30];
dSprintf(buf, sizeof(buf), "newfont_%d", smSheetIdCount++);
GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, GBitmap::Alpha);
// Set everything to transparent.
U8 *bits = bitmap->getWritableBits();
dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize);
TextureHandle handle = TextureHandle(buf, bitmap);
handle.setFilterNearest();
mTextureSheets.increment();
constructInPlace(&mTextureSheets.last());
mTextureSheets.last() = handle;
mCurX = 0;
mCurY = 0;
mCurSheet = mTextureSheets.size() - 1;
}
//////////////////////////////////////////////////////////////////////////
const PlatformFont::CharInfo &GFont::getCharInfo(const UTF16 in_charIndex)
{
PROFILE_START(NewFontGetCharInfo);
AssertFatal(in_charIndex, "GFont::getCharInfo - can't get info for char 0!");
if(mRemapTable[in_charIndex] == -1)
{
bool ret = loadCharInfo(in_charIndex);
}
AssertFatal(mRemapTable[in_charIndex] != -1, "No remap info for this character");
PROFILE_END();
return mCharInfoList[mRemapTable[in_charIndex]];
}
//////////////////////////////////////////////////////////////////////////
U32 GFont::getStrWidth(const UTF8* in_pString)
{
AssertFatal(in_pString != NULL, "GFont::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 GFont::getStrWidthPrecise(const UTF8* in_pString)
{
AssertFatal(in_pString != NULL, "GFont::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));
}
//////////////////////////////////////////////////////////////////////////
bool GFont::read(Stream& io_rStream)
{
// Handle versioning
U32 version;
io_rStream.read(&version);
if(version != csm_fileVersion)
return false;
char buf[256];
io_rStream.readString(buf);
mFaceName = StringTable->insert(buf);
io_rStream.read(&mSize);
io_rStream.read(&mCharSet);
io_rStream.read(&mHeight);
io_rStream.read(&mBaseline);
io_rStream.read(&mAscent);
io_rStream.read(&mDescent);
U32 size = 0;
io_rStream.read(&size);
mCharInfoList.setSize(size);
U32 i;
for(i = 0; i < size; i++)
{
PlatformFont::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);
ci->bitmapData = NULL;
}
U32 numSheets = 0;
io_rStream.read(&numSheets);
for(i = 0; i < numSheets; i++)
{
GBitmap *bmp = new GBitmap;
if(!bmp->readPNG(io_rStream))
{
delete bmp;
return false;
}
char buf[30];
dSprintf(buf, sizeof(buf), "font_%d", smSheetIdCount++);
mTextureSheets.increment();
constructInPlace(&mTextureSheets.last());
mTextureSheets.last() = TextureHandle(buf, bmp);
mTextureSheets.last().setFilterNearest();
}
// Read last position info
io_rStream.read(&mCurX);
io_rStream.read(&mCurY);
io_rStream.read(&mCurSheet);
// Read the remap table.
U32 minGlyph, maxGlyph;
io_rStream.read(&minGlyph);
io_rStream.read(&maxGlyph);
if(maxGlyph >= minGlyph)
{
// Length of buffer..
U32 buffLen;
io_rStream.read(&buffLen);
// Read the buffer.
FrameTemp<S32> inBuff(buffLen);
io_rStream.read(buffLen, inBuff);
// Decompress.
uLongf destLen = (maxGlyph-minGlyph+1)*sizeof(S32);
uncompress((Bytef*)&mRemapTable[minGlyph], &destLen, (Bytef*)(S32*)inBuff, buffLen);
AssertISV(destLen == (maxGlyph-minGlyph+1)*sizeof(S32), "GFont::read - invalid remap table data!");
// Make sure we've got the right endianness.
for(i = minGlyph; i <= maxGlyph; i++)
mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
}
return (io_rStream.getStatus() == Stream::Ok);
}
bool GFont::write(Stream& stream)
{
// Handle versioning
stream.write(csm_fileVersion);
// Write font info
stream.writeString(mFaceName);
stream.write(mSize);
stream.write(mCharSet);
stream.write(mHeight);
stream.write(mBaseline);
stream.write(mAscent);
stream.write(mDescent);
// Write char info list
stream.write(U32(mCharInfoList.size()));
U32 i;
for(i = 0; i < mCharInfoList.size(); i++)
{
const PlatformFont::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(mTextureSheets.size());
for(i = 0; i < mTextureSheets.size(); i++)
mTextureSheets[i].getBitmap()->writePNG(stream);
stream.write(mCurX);
stream.write(mCurY);
stream.write(mCurSheet);
// Get the min/max we have values for, and only write that range out.
S32 minGlyph = S32_MAX, maxGlyph = 0;
for(i = 0; i < 65536; i++)
{
if(mRemapTable[i] != -1)
{
if(i>maxGlyph) maxGlyph = i;
if(i<minGlyph) minGlyph = i;
}
}
stream.write(minGlyph);
stream.write(maxGlyph);
// Skip it if we don't have any glyphs to do...
if(maxGlyph >= minGlyph)
{
// Put everything big endian, to be consistent. Do this inplace.
for(i = minGlyph; i <= maxGlyph; i++)
mRemapTable[i] = convertHostToBEndian(mRemapTable[i]);
{
// Compress.
const U32 buffSize = 128 * 1024;
FrameTemp<S32> outBuff(buffSize);
uLongf destLen = buffSize * sizeof(S32);
compress2((Bytef*)(S32*)outBuff, &destLen, (Bytef*)(S32*)&mRemapTable[minGlyph], (maxGlyph-minGlyph+1)*sizeof(S32), 9);
// Write out.
stream.write((U32)destLen);
stream.write(destLen, outBuff);
}
// Put us back to normal.
for(i = minGlyph; i <= maxGlyph; i++)
mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
}
return (stream.getStatus() == Stream::Ok);
}
void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning)
{
// Figure dimensions of our strip by iterating over all the char infos.
U32 totalHeight = 0;
U32 totalWidth = 0;
S32 heightMin=0, heightMax=0;
for(S32 i=0; i<mCharInfoList.size(); i++)
{
totalWidth += mCharInfoList[i].width + kerning + 2*padding;
heightMin = getMin((S32)heightMin, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin);
heightMax = getMax((S32)heightMax, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin + (S32)mCharInfoList[i].height);
}
totalHeight = heightMax - heightMin + 2*padding;
// Make the bitmap.
GBitmap gb(totalWidth, totalHeight, false, mTextureSheets[0].getBitmap()->getFormat());
dMemset(gb.getWritableBits(), 0, sizeof(U8) * totalHeight * totalWidth );
// Ok, copy some rects, taking into account padding, kerning, offset.
U32 curWidth = kerning + padding;
for(S32 i=0; i<mCharInfoList.size(); i++)
{
// Skip invalid stuff.
if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
continue;
// Copy the rect.
U32 bitmap = mCharInfoList[i].bitmapIndex;
RectI ri(mCharInfoList[i].xOffset, mCharInfoList[i].yOffset, mCharInfoList[i].width, mCharInfoList[i].height );
Point2I outRi(curWidth, padding + getBaseline() - mCharInfoList[i].yOrigin);
gb.copyRect(mTextureSheets[bitmap].getBitmap(), ri, outRi);
// Advance.
curWidth += mCharInfoList[i].width + kerning + 2*padding;
}
// Write the image!
FileStream fs;
if(!ResourceManager->openFileForWrite(fs, fileName))
{
Con::errorf("GFont::exportStrip - failed to open '%s' for writing.", fileName);
return;
}
// Done!
gb.writePNG(fs, false);
}
/// Used for repacking in GFont::importStrip.
struct GlyphMap
{
U32 charId;
GBitmap *bitmap;
};
static S32 QSORT_CALLBACK GlyphMapCompare(const void *a, const void *b)
{
S32 ha = ((GlyphMap *) a)->bitmap->height;
S32 hb = ((GlyphMap *) b)->bitmap->height;
return hb - ha;
}
void GFont::importStrip(const char *fileName, U32 padding, U32 kerning)
{
// Wipe our texture sheets, and reload bitmap data from the specified file.
// Also deal with kerning.
// Also, we may have to load RGBA instead of RGB.
// Wipe our texture sheets.
mCurSheet = mCurX = mCurY = 0;
mTextureSheets.clear();
// Now, load the font strip.
GBitmap *strip = GBitmap::load(fileName);
if(!strip)
{
Con::errorf("GFont::importStrip - could not load file '%s'!", fileName);
return;
}
// And get parsing and copying - load up all the characters as separate
// GBitmaps, sort, then pack. Not terribly efficient but this is basically
// on offline task anyway.
// Ok, snag some glyphs.
Vector<GlyphMap> glyphList;
glyphList.reserve(mCharInfoList.size());
U32 curWidth = 0;
for(S32 i=0; i<mCharInfoList.size(); i++)
{
// Skip invalid stuff.
if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
continue;
// Allocate a new bitmap for this glyph, taking into account kerning and padding.
glyphList.increment();
glyphList.last().bitmap = new GBitmap(mCharInfoList[i].width + kerning + 2*padding, mCharInfoList[i].height + 2*padding, false, strip->getFormat());
glyphList.last().charId = i;
// Copy the rect.
RectI ri(curWidth, getBaseline() - mCharInfoList[i].yOrigin, glyphList.last().bitmap->width, glyphList.last().bitmap->height);
Point2I outRi(0,0);
glyphList.last().bitmap->copyRect(strip, ri, outRi);
// Update glyph attributes.
mCharInfoList[i].width = glyphList.last().bitmap->width;
mCharInfoList[i].height = glyphList.last().bitmap->height;
mCharInfoList[i].xOffset -= kerning + padding;
mCharInfoList[i].xIncrement += kerning;
mCharInfoList[i].yOffset -= padding;
// Advance.
curWidth += ri.extent.x;
}
// Ok, we have a big list of glyphmaps now. So let's sort them, then pack them.
dQsort(glyphList.address(), glyphList.size(), sizeof(GlyphMap), GlyphMapCompare);
// They're sorted by height, so now we can do some sort of awesome packing.
Point2I curSheetSize(256, 256);
Vector<U32> sheetSizes;
S32 curY = 0;
S32 curX = 0;
S32 curLnHeight = 0;
S32 maxHeight = 0;
for(U32 i = 0; i < glyphList.size(); i++)
{
PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
if(ci->height > maxHeight)
maxHeight = ci->height;
if(curX + ci->width > curSheetSize.x)
{
curY += curLnHeight;
curX = 0;
curLnHeight = 0;
}
if(curY + ci->height > curSheetSize.y)
{
sheetSizes.push_back(curSheetSize.y);
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;
}
// Terminate the packing loop calculations.
curY += curLnHeight;
if(curY < 64)
curSheetSize.y = 64;
else if(curY < 128)
curSheetSize.y = 128;
sheetSizes.push_back(curSheetSize.y);
if(getHeight() + padding * 2 > maxHeight)
maxHeight = getHeight() + padding * 2;
// Allocate texture pages.
for(S32 i=0; i<sheetSizes.size(); i++)
{
char buf[30];
dSprintf(buf, sizeof(buf), "newfont_%d", smSheetIdCount++);
GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, strip->getFormat());
// Set everything to transparent.
U8 *bits = bitmap->getWritableBits();
dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize * strip->bytesPerPixel);
TextureHandle handle = TextureHandle( buf, bitmap );
mTextureSheets.increment();
constructInPlace(&mTextureSheets.last());
mTextureSheets.last() = handle;
}
// Alright, we're ready to copy bits!
for(S32 i=0; i<glyphList.size(); i++)
{
// Copy each glyph into the appropriate place.
PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
U32 bi = ci->bitmapIndex;
mTextureSheets[bi].getBitmap()->copyRect(glyphList[i].bitmap, RectI(0,0, glyphList[i].bitmap->width,glyphList[i].bitmap->height), Point2I(ci->xOffset, ci->yOffset));
}
// Ok, all done! Just refresh some textures and we're set.
for(S32 i=0; i<sheetSizes.size(); i++)
mTextureSheets[i].refresh();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -