FontCache reading

This commit is contained in:
Jack Andersen 2015-11-28 11:45:38 -10:00
parent b1f99619cd
commit 4ede0d6858
4 changed files with 111 additions and 38 deletions

View File

@ -52,6 +52,8 @@ public:
operator FT_Face() {open(); return m_face;} operator FT_Face() {open(); return m_face;}
}; };
using FCharFilter = std::function<bool(uint32_t)>;
class FontAtlas class FontAtlas
{ {
friend class FontCache; friend class FontCache;
@ -70,7 +72,6 @@ public:
float m_uv[4]; float m_uv[4];
atInt32 m_leftPadding; atInt32 m_leftPadding;
atInt32 m_advance; atInt32 m_advance;
atInt32 m_rightPadding;
atUint32 m_width; atUint32 m_width;
atUint32 m_height; atUint32 m_height;
atInt32 m_verticalOffset; atInt32 m_verticalOffset;
@ -110,9 +111,9 @@ private:
public: public:
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
bool subpixel, Athena::io::FileWriter& writer); bool subpixel, FCharFilter& filter, Athena::io::FileWriter& writer);
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
bool subpixel, Athena::io::FileReader& reader); bool subpixel, FCharFilter& filter, Athena::io::FileReader& reader);
FontAtlas(const FontAtlas& other) = delete; FontAtlas(const FontAtlas& other) = delete;
FontAtlas& operator=(const FontAtlas& other) = delete; FontAtlas& operator=(const FontAtlas& other) = delete;
@ -168,17 +169,19 @@ public:
FontCache(const FontCache& other) = delete; FontCache(const FontCache& other) = delete;
FontCache& operator=(const FontCache& other) = delete; FontCache& operator=(const FontCache& other) = delete;
FontTag prepCustomFont(boo::IGraphicsDataFactory* gf, static bool DefaultCharFilter(uint32_t ch) {return true;}
const std::string& name, FT_Face face, bool subpixel=false,
FontTag prepCustomFont(boo::IGraphicsDataFactory* gf, const std::string& name, FT_Face face,
FCharFilter filter=DefaultCharFilter, bool subpixel=false,
float points=10.0, uint32_t dpi=72); float points=10.0, uint32_t dpi=72);
FontTag prepMainFont(boo::IGraphicsDataFactory* gf, FontTag prepMainFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=DefaultCharFilter,
bool subpixel=false, float points=10.0, uint32_t dpi=72) bool subpixel=false, float points=10.0, uint32_t dpi=72)
{return prepCustomFont(gf, "droidsans-permissive", m_regFace, subpixel, points, dpi);} {return prepCustomFont(gf, "droidsans-permissive", m_regFace, filter, subpixel, points, dpi);}
FontTag prepMonoFont(boo::IGraphicsDataFactory* gf, FontTag prepMonoFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=DefaultCharFilter,
bool subpixel=false, float points=10.0, uint32_t dpi=72) bool subpixel=false, float points=10.0, uint32_t dpi=72)
{return prepCustomFont(gf, "bmonofont", m_monoFace, subpixel, points, dpi);} {return prepCustomFont(gf, "bmonofont", m_monoFace, filter, subpixel, points, dpi);}
void closeBuiltinFonts() {m_regFace.close(); m_monoFace.close();} void closeBuiltinFonts() {m_regFace.close(); m_monoFace.close();}

View File

@ -6,6 +6,7 @@
#include <LogVisor/LogVisor.hpp> #include <LogVisor/LogVisor.hpp>
#include <Athena/MemoryReader.hpp> #include <Athena/MemoryReader.hpp>
#include <stdint.h> #include <stdint.h>
#include <zlib.h>
#include FT_GZIP_H #include FT_GZIP_H
#include FT_SYSTEM_H #include FT_SYSTEM_H
@ -165,8 +166,59 @@ void FontAtlas::buildKernTable(FT_Face face)
} }
} }
static void WriteCompressed(Athena::io::FileWriter& writer, const atUint8* data, size_t sz)
{
atUint8 compBuf[8192];
z_stream z = {};
deflateInit(&z, Z_DEFAULT_COMPRESSION);
z.next_in = (Bytef*)data;
z.avail_in = sz;
writer.writeUint32Big(sz);
while (z.avail_in)
{
z.next_out = compBuf;
z.avail_out = 8192;
deflate(&z, Z_NO_FLUSH);
writer.writeUBytes(compBuf, 8192 - z.avail_out);
}
int finishCycle = Z_OK;
while (finishCycle != Z_STREAM_END)
{
z.next_out = compBuf;
z.avail_out = 8192;
finishCycle = deflate(&z, Z_FINISH);
writer.writeUBytes(compBuf, 8192 - z.avail_out);
}
deflateEnd(&z);
}
static void ReadDecompressed(Athena::io::FileReader& reader, atUint8* data, size_t sz)
{
atUint8 compBuf[8192];
z_stream z = {};
inflateInit(&z);
z.next_out = data;
atUint32 targetSz = reader.readUint32Big();
z.avail_out = std::min(sz, size_t(targetSz));
size_t readSz;
while ((readSz = reader.readUBytesToBuf(compBuf, 8192)))
{
z.next_in = compBuf;
z.avail_in = readSz;
inflate(&z, Z_NO_FLUSH);
}
int finishCycle = Z_OK;
while (finishCycle != Z_STREAM_END)
finishCycle = inflate(&z, Z_FINISH);
inflateEnd(&z);
}
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
bool subpixel, Athena::io::FileWriter& writer) bool subpixel, FCharFilter& filter, Athena::io::FileWriter& writer)
: m_dpi(dpi), : m_dpi(dpi),
m_ftXscale(face->size->metrics.x_scale), m_ftXscale(face->size->metrics.x_scale),
m_ftXPpem(face->size->metrics.x_ppem) m_ftXPpem(face->size->metrics.x_ppem)
@ -187,6 +239,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
unsigned fullTexmapLayers = 0; unsigned fullTexmapLayers = 0;
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
++glyphCount; ++glyphCount;
FT_Load_Glyph(face, gindex, baseFlags); FT_Load_Glyph(face, gindex, baseFlags);
FT_UInt width, height; FT_UInt width, height;
@ -247,6 +304,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
fullTexmapLayers = 0; fullTexmapLayers = 0;
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags); FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
m_glyphLookup[charcode] = m_glyphs.size(); m_glyphLookup[charcode] = m_glyphs.size();
m_glyphs.emplace_back(); m_glyphs.emplace_back();
@ -262,7 +324,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight); g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6; g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6;
g.m_advance = face->glyph->advance.x >> 6; g.m_advance = face->glyph->advance.x >> 6;
g.m_rightPadding = 0;
g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6; g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6;
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
{ {
@ -284,7 +345,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
charcode = FT_Get_Next_Char(face, charcode, &gindex); charcode = FT_Get_Next_Char(face, charcode, &gindex);
} }
writer.writeUBytes((atUint8*)texmap.get(), bufSz); WriteCompressed(writer, (atUint8*)texmap.get(), bufSz);
m_tex = m_tex =
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
boo::TextureFormat::RGBA8, texmap.get(), bufSz); boo::TextureFormat::RGBA8, texmap.get(), bufSz);
@ -318,6 +379,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
fullTexmapLayers = 0; fullTexmapLayers = 0;
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags); FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
m_glyphLookup[charcode] = m_glyphs.size(); m_glyphLookup[charcode] = m_glyphs.size();
m_glyphs.emplace_back(); m_glyphs.emplace_back();
@ -333,7 +399,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight); g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6; g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6;
g.m_advance = face->glyph->advance.x >> 6; g.m_advance = face->glyph->advance.x >> 6;
g.m_rightPadding = 0;
g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6; g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6;
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
{ {
@ -355,7 +420,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
charcode = FT_Get_Next_Char(face, charcode, &gindex); charcode = FT_Get_Next_Char(face, charcode, &gindex);
} }
writer.writeUBytes((atUint8*)texmap.get(), bufSz); WriteCompressed(writer, (atUint8*)texmap.get(), bufSz);
m_tex = m_tex =
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
boo::TextureFormat::I8, texmap.get(), bufSz); boo::TextureFormat::I8, texmap.get(), bufSz);
@ -365,7 +430,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
} }
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
bool subpixel, Athena::io::FileReader& reader) bool subpixel, FCharFilter& filter, Athena::io::FileReader& reader)
: m_dpi(dpi), : m_dpi(dpi),
m_ftXscale(face->size->metrics.x_scale), m_ftXscale(face->size->metrics.x_scale),
m_ftXPpem(face->size->metrics.x_ppem) m_ftXPpem(face->size->metrics.x_ppem)
@ -382,6 +447,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
FT_ULong charcode = FT_Get_First_Char(face, &gindex); FT_ULong charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
++glyphCount; ++glyphCount;
charcode = FT_Get_Next_Char(face, charcode, &gindex); charcode = FT_Get_Next_Char(face, charcode, &gindex);
} }
@ -421,9 +491,15 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
fullTexmapLayers = 0; fullTexmapLayers = 0;
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
FT_Load_Glyph(face, gindex, baseFlags); FT_Load_Glyph(face, gindex, baseFlags);
FT_UInt width, height; FT_UInt width, height;
GridFitGlyph(face->glyph, width, height); GridFitGlyph(face->glyph, width, height);
m_glyphLookup[charcode] = m_glyphs.size();
m_glyphs.emplace_back(); m_glyphs.emplace_back();
Glyph& g = m_glyphs.back(); Glyph& g = m_glyphs.back();
g.m_unicodePoint = charcode; g.m_unicodePoint = charcode;
@ -437,7 +513,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight); g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6; g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6;
g.m_advance = face->glyph->advance.x >> 6; g.m_advance = face->glyph->advance.x >> 6;
g.m_rightPadding = 0;
g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6; g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6;
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
{ {
@ -458,7 +533,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
charcode = FT_Get_Next_Char(face, charcode, &gindex); charcode = FT_Get_Next_Char(face, charcode, &gindex);
} }
reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz); ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz);
m_tex = m_tex =
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
boo::TextureFormat::RGBA8, texmap.get(), bufSz); boo::TextureFormat::RGBA8, texmap.get(), bufSz);
@ -492,9 +567,15 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
fullTexmapLayers = 0; fullTexmapLayers = 0;
while (gindex != 0) while (gindex != 0)
{ {
if (!filter(charcode))
{
charcode = FT_Get_Next_Char(face, charcode, &gindex);
continue;
}
FT_Load_Glyph(face, gindex, baseFlags); FT_Load_Glyph(face, gindex, baseFlags);
FT_UInt width, height; FT_UInt width, height;
GridFitGlyph(face->glyph, width, height); GridFitGlyph(face->glyph, width, height);
m_glyphLookup[charcode] = m_glyphs.size();
m_glyphs.emplace_back(); m_glyphs.emplace_back();
Glyph& g = m_glyphs.back(); Glyph& g = m_glyphs.back();
g.m_unicodePoint = charcode; g.m_unicodePoint = charcode;
@ -508,7 +589,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight); g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6; g.m_leftPadding = face->glyph->metrics.horiBearingX >> 6;
g.m_advance = face->glyph->advance.x >> 6; g.m_advance = face->glyph->advance.x >> 6;
g.m_rightPadding = 0;
g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6; g.m_verticalOffset = (face->glyph->metrics.horiBearingY - face->glyph->metrics.height) >> 6;
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
{ {
@ -529,7 +609,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
charcode = FT_Get_Next_Char(face, charcode, &gindex); charcode = FT_Get_Next_Char(face, charcode, &gindex);
} }
reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz); ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz);
m_tex = m_tex =
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
boo::TextureFormat::I8, texmap.get(), bufSz); boo::TextureFormat::I8, texmap.get(), bufSz);
@ -557,8 +637,8 @@ FontCache::FontCache(const HECL::Runtime::FileStoreManager& fileMgr)
m_monoFace(m_fontLib, BMONOFONT, BMONOFONT_SZ) m_monoFace(m_fontLib, BMONOFONT, BMONOFONT_SZ)
{HECL::MakeDir(m_cacheRoot.c_str());} {HECL::MakeDir(m_cacheRoot.c_str());}
FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, const std::string& name, FT_Face face,
const std::string& name, FT_Face face, bool subpixel, FCharFilter filter, bool subpixel,
float points, uint32_t dpi) float points, uint32_t dpi)
{ {
/* Quick validation */ /* Quick validation */
@ -579,29 +659,27 @@ FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf,
/* Now check filesystem cache */ /* Now check filesystem cache */
HECL::SystemString cachePath = m_cacheRoot + _S('/') + HECL::SysFormat(_S("%" PRIx64), tag.hash()); HECL::SystemString cachePath = m_cacheRoot + _S('/') + HECL::SysFormat(_S("%" PRIx64), tag.hash());
#if 0
HECL::Sstat st; HECL::Sstat st;
if (!HECL::Stat(cachePath.c_str(), &st) && S_ISREG(st.st_mode)) if (!HECL::Stat(cachePath.c_str(), &st) && S_ISREG(st.st_mode))
{ {
Athena::io::FileReader r(cachePath); Athena::io::FileReader r(cachePath);
if (!r.hasError()) if (!r.hasError())
{ {
atUint32 magic = r.readUint32Big();b atUint32 magic = r.readUint32Big();
if (r.position() == 4 && magic == 'FONT') if (r.position() == 4 && magic == 'FONT')
{ {
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, r)); m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, filter, r));
return tag; return tag;
} }
} }
} }
#endif
/* Nada, build and cache now */ /* Nada, build and cache now */
Athena::io::FileWriter w(cachePath); Athena::io::FileWriter w(cachePath);
if (w.hasError()) if (w.hasError())
Log.report(LogVisor::FatalError, "unable to open '%s' for writing", cachePath.c_str()); Log.report(LogVisor::FatalError, "unable to open '%s' for writing", cachePath.c_str());
w.writeUint32Big('FONT'); w.writeUint32Big('FONT');
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, w)); m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, filter, w));
return tag; return tag;
} }

View File

@ -7,8 +7,8 @@ static LogVisor::LogModule Log("Specter");
void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache) void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache)
{ {
m_factory = factory; m_factory = factory;
m_mainFont = fcache->prepMainFont(factory, false, 10.0, 72); m_mainFont = fcache->prepMainFont(factory, FontCache::DefaultCharFilter, false, 10.0, 72);
m_monoFont = fcache->prepMonoFont(factory, false, 10.0, 72); m_monoFont = fcache->prepMonoFont(factory, FontCache::DefaultCharFilter, false, 10.0, 72);
switch (factory->platform()) switch (factory->platform())
{ {
case boo::IGraphicsDataFactory::Platform::OGL: case boo::IGraphicsDataFactory::Platform::OGL:

View File

@ -361,12 +361,8 @@ void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& default
} }
if (lCh != -1) if (lCh != -1)
{
adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas); adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas);
m_glyphs.emplace_back(adv, *glyph, defaultColor); m_glyphs.emplace_back(adv, *glyph, defaultColor);
}
else
m_glyphs.emplace_back(adv, *glyph, defaultColor);
lCh = ch; lCh = ch;
rem -= sz; rem -= sz;
@ -395,12 +391,8 @@ void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaul
continue; continue;
if (lCh != -1) if (lCh != -1)
{
adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas); adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas);
m_glyphs.emplace_back(adv, *glyph, defaultColor); m_glyphs.emplace_back(adv, *glyph, defaultColor);
}
else
m_glyphs.emplace_back(adv, *glyph, defaultColor);
lCh = ch; lCh = ch;