Skip to content

Instantly share code, notes, and snippets.

@jamesu
Created October 9, 2013 16:22
Show Gist options
  • Save jamesu/6903946 to your computer and use it in GitHub Desktop.
Save jamesu/6903946 to your computer and use it in GitHub Desktop.
FreeType Font code for Torque2D. Should also work with modifications for Torque3D
//-----------------------------------------------------------------------------
// Copyright(C) 2013 James S Urquhart.
// Derived from code from T2D MIT, Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/FTFont.h"
#include "dgl/gFont.h"
#include "dgl/gBitmap.h"
#include "math/mRect.h"
#include "console/console.h"
#include "core/unicode.h"
#include "core/frameAllocator.h"
#define FT_FLOOR(X) ((X & -64) >> 6)
#define FT_CEIL(X) (((X + 63) & -64) >> 6)
static FT_Library sFTLibrary;
bool sFTInit = false;
typedef struct EnumFontEntry {
StringTableEntry mName;
StringTableEntry mFilename;
} EnumFontEntry;
Vector<EnumFontEntry> sFTFontList;
static void EnumerateTTFonts()
{
Vector<Platform::FileInfo> list;
Platform::dumpPath("ttf", list, 0);
for (Vector<Platform::FileInfo>::iterator itr = list.begin(); itr != list.end(); itr++)
{
Con::printf("Font: %s/%s...", itr->pFullPath, itr->pFileName);
char ttfName[4096];
dSprintf(ttfName, 4096, "%s/%s", itr->pFullPath, itr->pFileName);
// Load it
FT_Face face;
FT_SfntName sn,qn,zn;
int error = FT_New_Face(sFTLibrary, ttfName, 0, &face);
if (error != 0)
continue;
int count = FT_Get_Sfnt_Name_Count(face);
char name[1024];
char *name1 = NULL;
char *name2 = NULL;
for(int i=0; i<count; i++) {
if(FT_Get_Sfnt_Name(face,i,&qn) != 0 )
break;
if(dStrlen(qn.string) == 0 )
continue;
//Con::printf("Name: '%s' (%x)\n", qn.string, qn.name_id);
switch(qn.name_id) {
case TT_NAME_ID_FONT_FAMILY:
if (name1 == NULL)
name1=strndup(qn.string,qn.string_len);
break;
case TT_NAME_ID_PS_NAME:
if(name1 == NULL)
name1=strndup(qn.string,qn.string_len);
break;
case TT_NAME_ID_PREFERRED_FAMILY:
if (name1 != NULL)
dFree(name1);
name1=strndup(qn.string,qn.string_len);
break;
case TT_NAME_ID_PREFERRED_SUBFAMILY:
if (name2 != NULL)
dFree(name2);
name2=strndup(qn.string,qn.string_len);
break;
case TT_NAME_ID_FONT_SUBFAMILY:
if (name2 == NULL)
name2=strndup(qn.string,qn.string_len);
break;
default:
continue;
}
}
FT_Get_Sfnt_Name( face, TT_NAME_ID_FONT_SUBFAMILY, &zn );
if( !zn.string || !qn.string || qn.string_len <= 0 || zn.string_len <= 0 )
{
FT_Done_Face(face);
return false;
}
int n1 = dStrlen(name1);
int n2 = n1 + dStrlen(name2);
dSprintf( name, 1024 ,"%s %s", name1, name2);
Con::printf("Registered %s as %s", name, ttfName);
EnumFontEntry entry;
entry.mFilename = StringTable->insert(ttfName);
entry.mName = StringTable->insert(name);
sFTFontList.push_back(entry);
// If we have "Regular" or "Roman", set that as the default
if (dStricmp(name2, "Regular") == 0 || dStricmp(name2, "Roman") == 0)
{
Con::printf("Registered %s as %s", name1, ttfName);
entry.mName = StringTable->insert(name1);
sFTFontList.push_back(entry);
}
FT_Done_Face(face);
}
}
static StringTableEntry ResolveTTFont(const char *name)
{
StringTableEntry entry = StringTable->insert(name);
for (Vector<EnumFontEntry>::iterator itr = sFTFontList.begin(); itr != sFTFontList.end(); itr++)
{
if (itr->mName == entry)
{
return itr->mFilename;
}
}
return NULL;
}
//------------------------------------------------------------------------------
// New Unicode capable font class.
PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
{
PlatformFont *retFont = new FTFont;
if(retFont->create(name, size, charset))
return retFont;
delete retFont;
return NULL;
}
//------------------------------------------------------------------------------
FTFont::FTFont()
{
mInitFace = false;
}
FTFont::~FTFont()
{
if (mInitFace)
{
FT_Done_Face(mFace);
}
}
//------------------------------------------------------------------------------
bool FTFont::create( const char *name, U32 size, U32 charset)
{
int error = 0;
// Init freetype
if (!sFTInit)
{
error = FT_Init_FreeType(&sFTLibrary);
if (error)
{
Con::errorf("Error: could not init freetype");
return false;
}
// Get a list of available ttf fonts
EnumerateTTFonts();
sFTInit = true;
}
// Resolve font name
StringTableEntry font = ResolveTTFont(name);
if (font == NULL)
{
Con::errorf("Could not find font '%s'", name);
return false;
}
error = FT_New_Face(sFTLibrary, font, 0, &mFace);
if (error)
{
Con::errorf("Error: could not find ttf for font '%s'", name);
return false;
}
// Set backing buffer size
error = FT_Set_Pixel_Sizes(mFace, 0, size /**(GFont::sFontGenerateScale)*/);
mHeight = (mFace->size->metrics.height + 32) >> 6;
mAscent = (mFace->size->metrics.ascender + 32) >> 6;
mName = StringTable->insert(name);
mSize = size;
mHeight = ((mFace->size->metrics.ascender + -mFace->size->metrics.descender) + 32) >> 6;
return true;
}
//------------------------------------------------------------------------------
bool FTFont::isValidChar(const UTF8 *str) const
{
// since only low order characters are invalid, and since those characters
// are single codeunits in UTF8, we can safely cast here.
return isValidChar((UTF16)*str);
}
bool FTFont::isValidChar( const UTF16 ch) const
{
// We cut out the ASCII control chars here. Only printable characters are valid.
// 0x20 == 32 == space
if( ch < 0x20 )
return false;
return true;
}
PlatformFont::CharInfo& FTFont::getCharInfo(const UTF8 *str) const
{
return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL)));
}
static PlatformFont::CharInfo sCharInfo;
PlatformFont::CharInfo& FTFont::getCharInfo(const UTF16 ch) const
{
dMemset(&sCharInfo, 0, sizeof(sCharInfo));
int error = 0;
//sCharInfo.bitmapIndex = -1;
FT_UInt gindex = FT_Get_Char_Index(mFace, ch);
sCharInfo.bitmapIndex = 0;
sCharInfo.xOrigin = 0;
sCharInfo.yOrigin = 0;
sCharInfo.xIncrement = FT_CEIL(mFace->size->metrics.max_advance) >> 6;
if (FT_Load_Glyph(mFace, gindex, FT_LOAD_RENDER/* | FT_LOAD_FORCE_AUTOHINT*/) == 0)
{
FT_Pos left = FT_FLOOR(mFace->glyph->metrics.horiBearingX);
FT_Pos top = FT_CEIL(mFace->glyph->metrics.horiBearingY);
//error = FT_Render_Glyph(mFace->glyph,
// FT_RENDER_MODE_NORMAL);
if (error == 0)
{
U32 bitmapDataSize = mFace->glyph->bitmap.width * mFace->glyph->bitmap.rows;
if (bitmapDataSize != 0)
{
sCharInfo.bitmapData = new U8[bitmapDataSize];
U8 *src = mFace->glyph->bitmap.buffer;
U8 *dst = sCharInfo.bitmapData;
for (int i=0; i<mFace->glyph->bitmap.rows; i++)
{
dMemcpy(dst, src, mFace->glyph->bitmap.width);
dst += mFace->glyph->bitmap.width;
src += mFace->glyph->bitmap.pitch;
}
sCharInfo.height = mFace->glyph->bitmap.rows;
sCharInfo.width = mFace->glyph->bitmap.width;
}
else
{
sCharInfo.bitmapIndex = 0;
}
sCharInfo.xOrigin = left;//(left + 32) >> 6;
sCharInfo.yOrigin = top;//(top + 32) >> 6;
sCharInfo.xIncrement = FT_CEIL(mFace->glyph->metrics.horiAdvance);//(mFace->glyph->metrics.horiAdvance + 32) >> 6;
}
}
return sCharInfo;
}
//-----------------------------------------------------------------------------
// Copyright(C) 2013 James S Urquhart.
// Derived from code from T2D MIT, Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platformFont.h"
extern "C"
{
#include <ft2build.h>
#include FT_FREETYPE_H
#include "freetype/ftsnames.h"
#include "freetype/ttnameid.h"
#include "freetype/ftwinfnt.h"
}
class FTFont : public PlatformFont
{
private:
FT_Face mFace;
bool mInitFace;
// Cache the baseline and height for the getter methods below.
U32 mHeight; // distance between lines
U32 mAscent;
// Cache the size and name requested in create()
U32 mSize;
StringTableEntry mName;
public:
FTFont();
virtual ~FTFont();
/// Look up the requested font, cache style, layout, colorspace, and some metrics.
virtual bool create( const char* name, U32 size, U32 charset = TGE_ANSI_CHARSET);
/// Determine if the character requested is a drawable character, or if it should be ignored.
virtual bool isValidChar( const UTF16 ch) const;
virtual bool isValidChar( const UTF8 *str) const;
/// Get some vertical data on the font at large. Useful for drawing multiline text, and sizing text boxes.
virtual U32 getFontHeight() const;
virtual U32 getFontBaseLine() const;
// Draw the character to a temporary bitmap, and fill the CharInfo with various text metrics.
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const;
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const;
};
inline U32 FTFont::getFontHeight() const
{
return mHeight;
}
inline U32 FTFont::getFontBaseLine() const
{
return mAscent;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment