From 4ede0d68588096dcda1ae0960598964679c06a27 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 28 Nov 2015 11:45:38 -1000 Subject: [PATCH] FontCache reading --- specter/include/Specter/FontCache.hpp | 21 ++--- specter/lib/FontCache.cpp | 112 ++++++++++++++++++++++---- specter/lib/Specter.cpp | 4 +- specter/lib/TextView.cpp | 12 +-- 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/specter/include/Specter/FontCache.hpp b/specter/include/Specter/FontCache.hpp index 720151176..4ba78b5d3 100644 --- a/specter/include/Specter/FontCache.hpp +++ b/specter/include/Specter/FontCache.hpp @@ -52,6 +52,8 @@ public: operator FT_Face() {open(); return m_face;} }; +using FCharFilter = std::function; + class FontAtlas { friend class FontCache; @@ -70,7 +72,6 @@ public: float m_uv[4]; atInt32 m_leftPadding; atInt32 m_advance; - atInt32 m_rightPadding; atUint32 m_width; atUint32 m_height; atInt32 m_verticalOffset; @@ -110,9 +111,9 @@ private: public: 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, - bool subpixel, Athena::io::FileReader& reader); + bool subpixel, FCharFilter& filter, Athena::io::FileReader& reader); FontAtlas(const FontAtlas& other) = delete; FontAtlas& operator=(const FontAtlas& other) = delete; @@ -168,17 +169,19 @@ public: FontCache(const FontCache& other) = delete; FontCache& operator=(const FontCache& other) = delete; - FontTag prepCustomFont(boo::IGraphicsDataFactory* gf, - const std::string& name, FT_Face face, bool subpixel=false, + static bool DefaultCharFilter(uint32_t ch) {return true;} + + 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); - FontTag prepMainFont(boo::IGraphicsDataFactory* gf, + FontTag prepMainFont(boo::IGraphicsDataFactory* gf, FCharFilter filter=DefaultCharFilter, 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) - {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();} diff --git a/specter/lib/FontCache.cpp b/specter/lib/FontCache.cpp index 50903424b..fcf8c39ef 100644 --- a/specter/lib/FontCache.cpp +++ b/specter/lib/FontCache.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include FT_GZIP_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, - bool subpixel, Athena::io::FileWriter& writer) + bool subpixel, FCharFilter& filter, Athena::io::FileWriter& writer) : m_dpi(dpi), m_ftXscale(face->size->metrics.x_scale), 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; while (gindex != 0) { + if (!filter(charcode)) + { + charcode = FT_Get_Next_Char(face, charcode, &gindex); + continue; + } ++glyphCount; FT_Load_Glyph(face, gindex, baseFlags); FT_UInt width, height; @@ -247,6 +304,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, fullTexmapLayers = 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); m_glyphLookup[charcode] = m_glyphs.size(); 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_leftPadding = face->glyph->metrics.horiBearingX >> 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; 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); } - writer.writeUBytes((atUint8*)texmap.get(), bufSz); + WriteCompressed(writer, (atUint8*)texmap.get(), bufSz); m_tex = gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, boo::TextureFormat::RGBA8, texmap.get(), bufSz); @@ -318,6 +379,11 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, fullTexmapLayers = 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); m_glyphLookup[charcode] = m_glyphs.size(); 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_leftPadding = face->glyph->metrics.horiBearingX >> 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; 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); } - writer.writeUBytes((atUint8*)texmap.get(), bufSz); + WriteCompressed(writer, (atUint8*)texmap.get(), bufSz); m_tex = gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, 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, - bool subpixel, Athena::io::FileReader& reader) + bool subpixel, FCharFilter& filter, Athena::io::FileReader& reader) : m_dpi(dpi), m_ftXscale(face->size->metrics.x_scale), 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); while (gindex != 0) { + if (!filter(charcode)) + { + charcode = FT_Get_Next_Char(face, charcode, &gindex); + continue; + } ++glyphCount; 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; while (gindex != 0) { + if (!filter(charcode)) + { + charcode = FT_Get_Next_Char(face, charcode, &gindex); + continue; + } FT_Load_Glyph(face, gindex, baseFlags); FT_UInt width, height; GridFitGlyph(face->glyph, width, height); + m_glyphLookup[charcode] = m_glyphs.size(); m_glyphs.emplace_back(); Glyph& g = m_glyphs.back(); 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_leftPadding = face->glyph->metrics.horiBearingX >> 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; 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); } - reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz); + ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz); m_tex = gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, boo::TextureFormat::RGBA8, texmap.get(), bufSz); @@ -492,9 +567,15 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, fullTexmapLayers = 0; while (gindex != 0) { + if (!filter(charcode)) + { + charcode = FT_Get_Next_Char(face, charcode, &gindex); + continue; + } FT_Load_Glyph(face, gindex, baseFlags); FT_UInt width, height; GridFitGlyph(face->glyph, width, height); + m_glyphLookup[charcode] = m_glyphs.size(); m_glyphs.emplace_back(); Glyph& g = m_glyphs.back(); 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_leftPadding = face->glyph->metrics.horiBearingX >> 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; 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); } - reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz); + ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz); m_tex = gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, boo::TextureFormat::I8, texmap.get(), bufSz); @@ -557,8 +637,8 @@ FontCache::FontCache(const HECL::Runtime::FileStoreManager& fileMgr) m_monoFace(m_fontLib, BMONOFONT, BMONOFONT_SZ) {HECL::MakeDir(m_cacheRoot.c_str());} -FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, - const std::string& name, FT_Face face, bool subpixel, +FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, const std::string& name, FT_Face face, + FCharFilter filter, bool subpixel, float points, uint32_t dpi) { /* Quick validation */ @@ -579,29 +659,27 @@ FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, /* Now check filesystem cache */ HECL::SystemString cachePath = m_cacheRoot + _S('/') + HECL::SysFormat(_S("%" PRIx64), tag.hash()); -#if 0 HECL::Sstat st; if (!HECL::Stat(cachePath.c_str(), &st) && S_ISREG(st.st_mode)) { Athena::io::FileReader r(cachePath); if (!r.hasError()) { - atUint32 magic = r.readUint32Big();b + atUint32 magic = r.readUint32Big(); if (r.position() == 4 && magic == 'FONT') { - m_cachedAtlases.emplace(tag, std::make_unique(gf, face, dpi, subpixel, r)); + m_cachedAtlases.emplace(tag, std::make_unique(gf, face, dpi, subpixel, filter, r)); return tag; } } } -#endif /* Nada, build and cache now */ Athena::io::FileWriter w(cachePath); if (w.hasError()) Log.report(LogVisor::FatalError, "unable to open '%s' for writing", cachePath.c_str()); w.writeUint32Big('FONT'); - m_cachedAtlases.emplace(tag, std::make_unique(gf, face, dpi, subpixel, w)); + m_cachedAtlases.emplace(tag, std::make_unique(gf, face, dpi, subpixel, filter, w)); return tag; } diff --git a/specter/lib/Specter.cpp b/specter/lib/Specter.cpp index f16a54fbb..af5d5a5a7 100644 --- a/specter/lib/Specter.cpp +++ b/specter/lib/Specter.cpp @@ -7,8 +7,8 @@ static LogVisor::LogModule Log("Specter"); void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache) { m_factory = factory; - m_mainFont = fcache->prepMainFont(factory, false, 10.0, 72); - m_monoFont = fcache->prepMonoFont(factory, false, 10.0, 72); + m_mainFont = fcache->prepMainFont(factory, FontCache::DefaultCharFilter, false, 10.0, 72); + m_monoFont = fcache->prepMonoFont(factory, FontCache::DefaultCharFilter, false, 10.0, 72); switch (factory->platform()) { case boo::IGraphicsDataFactory::Platform::OGL: diff --git a/specter/lib/TextView.cpp b/specter/lib/TextView.cpp index 5a5705674..292c3c3dc 100644 --- a/specter/lib/TextView.cpp +++ b/specter/lib/TextView.cpp @@ -361,12 +361,8 @@ void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& default } if (lCh != -1) - { adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas); - m_glyphs.emplace_back(adv, *glyph, defaultColor); - } - else - m_glyphs.emplace_back(adv, *glyph, defaultColor); + m_glyphs.emplace_back(adv, *glyph, defaultColor); lCh = ch; rem -= sz; @@ -395,12 +391,8 @@ void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaul continue; if (lCh != -1) - { adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas); - m_glyphs.emplace_back(adv, *glyph, defaultColor); - } - else - m_glyphs.emplace_back(adv, *glyph, defaultColor); + m_glyphs.emplace_back(adv, *glyph, defaultColor); lCh = ch;