#include #include #include #include FT_FREETYPE_H #include FT_GLYPH_H using namespace mitsuba; static const uint32_t KFontGlyphScale = 6; static const uint32_t KFontGlyphSpacing = 5; struct Glyph { Point2 uv; Vector2 uvSize; Vector2i pxSize; int horizontalAdvance; int horizontalBearing; int verticalBearing; inline Glyph() { } inline Glyph(Stream *stream) { uv = Point2( stream->readSingle(), stream->readSingle() ); uvSize = Vector2( stream->readSingle(), stream->readSingle() ); pxSize = Vector2i(stream); horizontalAdvance = stream->readInt(); horizontalBearing = stream->readInt(); verticalBearing = stream->readInt(); } void serialize(Stream *stream) { stream->writeSingle(uv.x); stream->writeSingle(uv.y); stream->writeSingle(uvSize.x); stream->writeSingle(uvSize.y); stream->writeInt(pxSize.x); stream->writeInt(pxSize.y); stream->writeInt(horizontalBearing); stream->writeInt(verticalBearing); stream->writeInt(horizontalAdvance); } }; int main(int argc, char **argv) { Class::staticInitialization(); Object::staticInitialization(); Thread::staticInitialization(); Logger::staticInitialization(); if (argc < 5) { cout << "Syntax: fontgen " << endl; return 0; } FT_Library library; if (FT_Init_FreeType(&library)) SLog(EError, "Error initializing the FreeType library"); FT_Face face; if (FT_New_Face(library, argv[1], 0, &face)) SLog(EError, "Unable to load font"); std::string name = FT_Get_Postscript_Name(face); cout << "Font name : " << name << endl; cout << "Glyph count : " << face->num_glyphs << endl; /* Set the glyph height */ int fontSize = atoi(argv[4]); if (FT_Set_Pixel_Sizes(face, 0, fontSize)) SLog(EError, "Error while setting character size"); int8_t *kerningMatrix = new int8_t[256*256]; uint32_t maxWidth = 0, maxHeight = 0, maxVertBearing = 0; cout << endl; memset(kerningMatrix, 0, 256*256); cout << "Analyzing.." << endl; for (int i=0; i<255; ++i) { // int index = FT_Get_Char_Index(face, i); int index = i; if (FT_Load_Char(face, i, FT_LOAD_RENDER)) continue; uint32_t height = face->glyph->metrics.height >> KFontGlyphScale; uint32_t width = face->glyph->metrics.width >> KFontGlyphScale; uint32_t vertBearing = face->glyph->metrics.horiBearingY >> KFontGlyphScale; for (uint32_t o=0; o<256; o++) { FT_Vector kerning; // int index2 = FT_Get_Char_Index(face, o); int index2 = o; FT_Get_Kerning(face, index, index2, FT_KERNING_DEFAULT, &kerning); kerningMatrix[i + o*256] = kerning.x >> KFontGlyphScale; } maxWidth = std::max(maxWidth, width); maxHeight = std::max(maxWidth, height); if (vertBearing <= fontSize) maxVertBearing = std::max(maxVertBearing, vertBearing); } cout << "Max. width : " << maxWidth << endl; cout << "Max. height : " << maxHeight << endl; cout << "Max. vbear : " << maxVertBearing << endl; /* 5 pixels spacing to avoid interpolation artefacts */ maxWidth += KFontGlyphSpacing; maxHeight += KFontGlyphSpacing; /* Calculate the total required area in square pixels */ uint32_t area = 256 * maxWidth * maxHeight; /* Side length of the square bitmap */ uint32_t finalSide = 0, side = (uint32_t) sqrtf(area); /* Safety margin */ side += maxWidth > maxHeight ? maxWidth : maxHeight; /* Round up to a power of two */ for (int i=0; i<12 && finalSide < side; i++) { finalSide = 1 << i; } if (finalSide < side) SLog(EError, "The requested font bitmap is bigger than 4096x4096!"); cout << "Final res. : " << finalSide << endl; /* Number of glyph tiles in each row */ uint32_t xcount = (uint32_t) std::floor((float) finalSide / (float) maxWidth); /* Create an bitmap with alpha */ ref bitmap = new Bitmap(Bitmap::ELuminance, Bitmap::EUInt8, Vector2i(finalSide)); bitmap->clear(); uint8_t *data = bitmap->getUInt8Data(); ref fs = new FileStream(argv[3], FileStream::ETruncReadWrite); fs->setByteOrder(Stream::ENetworkByteOrder); for (uint32_t i=0; i<256; i++) { //int index = FT_Get_Char_Index(face, i); int index = i; if (FT_Load_Char(face, index, FT_LOAD_RENDER)) continue; /* Calculate the tile index in the texture */ uint32_t ytile = i / xcount; uint32_t xtile = i % xcount; /* Calculate the position in the texture */ uint32_t xpos = xtile * maxWidth; uint32_t ypos = ytile * maxHeight; uint8_t *buffer = face->glyph->bitmap.buffer; uint32_t width = face->glyph->bitmap.width; uint32_t height = face->glyph->bitmap.rows; for (uint32_t j=0; jglyph->metrics; /* Retrieve further glyph metrics */ // glyph.horizontalBearing = (int) (metrics.horiBearingX / 64.0f); glyph.horizontalBearing = (int) face->glyph->bitmap_left; glyph.horizontalAdvance = (int) face->glyph->advance.x / 64; // glyph.horizontalAdvance = (int) (metrics.horiAdvance / 64.0f); glyph.verticalBearing = (int) (metrics.horiBearingY / 64.0f); glyph.serialize(fs); } fs->write(kerningMatrix, sizeof(int8_t)*256*256); fs->close(); fs = new FileStream(argv[2], FileStream::ETruncReadWrite); bitmap->write(Bitmap::EPNG, fs); fs->close(); delete[] kerningMatrix; FT_Done_Face(face); FT_Done_FreeType(library); Class::staticShutdown(); Object::staticShutdown(); Thread::staticShutdown(); return 0; }