diff --git a/include/llmr/text/glyph_store.hpp b/include/llmr/text/glyph_store.hpp index 1cd2fd539..c2d467e78 100644 --- a/include/llmr/text/glyph_store.hpp +++ b/include/llmr/text/glyph_store.hpp @@ -17,9 +17,6 @@ namespace llmr { class SDFGlyph { public: - SDFGlyph(); - SDFGlyph(pbf data); - // A signed distance field of the glyph with a border of 3 pixels. std::string bitmap; @@ -27,21 +24,27 @@ public: GlyphMetrics metrics; }; - -class GlyphSet { +class FontStack { public: - GlyphSet(const std::string &fontStack, GlyphRange glyphRange); - - void parse(); - - std::shared_future getFuture(); + void insert(uint32_t id, const SDFGlyph &glyph); private: std::map glyphs; + std::mutex mtx; +}; +class GlyphPBF { +public: + GlyphPBF(const std::string &fontStack, GlyphRange glyphRange); + + void parse(FontStack &stack); + + std::shared_future getFuture(); + +private: std::string data; - std::promise promise; - std::shared_future future; + std::promise promise; + std::shared_future future; std::mutex mtx; }; @@ -53,10 +56,11 @@ public: private: // Loads an individual glyph range from the font stack and adds it to rangeSets - std::shared_future loadGlyphRange(const std::string &fontStack, std::map> &rangeSets, GlyphRange range); + std::shared_future loadGlyphRange(const std::string &fontStack, std::map> &rangeSets, GlyphRange range); private: - std::unordered_map>> stacks; + std::unordered_map>> ranges; + std::unordered_map> stacks; std::mutex mtx; }; diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index fae9d1326..65fe38bc4 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -2,13 +2,20 @@ #include #include +#include #include #include namespace llmr { -GlyphSet::GlyphSet(const std::string &fontStack, GlyphRange glyphRange) +void FontStack::insert(uint32_t id, const SDFGlyph &glyph) { + std::lock_guard lock(mtx); + glyphs.emplace(id, glyph); +} + + +GlyphPBF::GlyphPBF(const std::string &fontStack, GlyphRange glyphRange) : future(promise.get_future().share()) { // Load the glyph set URL @@ -33,11 +40,11 @@ GlyphSet::GlyphSet(const std::string &fontStack, GlyphRange glyphRange) }); } -std::shared_future GlyphSet::getFuture() { +std::shared_future GlyphPBF::getFuture() { return future; } -void GlyphSet::parse() { +void GlyphPBF::parse(FontStack &stack) { std::lock_guard lock(mtx); if (!data.size()) { @@ -46,8 +53,48 @@ void GlyphSet::parse() { return; } - // TODO: parse the glyphs + // Parse the glyph PBF + pbf glyphs_pbf(reinterpret_cast(data.data()), data.size()); + while (glyphs_pbf.next()) { + if (glyphs_pbf.tag == 1) { // stacks + pbf fontstack_pbf = glyphs_pbf.message(); + while (fontstack_pbf.next()) { + if (fontstack_pbf.tag == 3) { // glyphs + pbf glyph_pbf = fontstack_pbf.message(); + + SDFGlyph glyph; + uint32_t id = 0; + + while (glyph_pbf.next()) { + if (glyph_pbf.tag == 1) { // id + id = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 2) { // bitmap + glyph.bitmap = glyph_pbf.string(); + } else if (glyph_pbf.tag == 3) { // width + glyph.metrics.width = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 4) { // height + glyph.metrics.height = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 5) { // left + glyph.metrics.left = glyph_pbf.svarint(); + } else if (glyph_pbf.tag == 6) { // top + glyph.metrics.top = glyph_pbf.svarint(); + } else if (glyph_pbf.tag == 7) { // advance + glyph.metrics.advance = glyph_pbf.varint(); + } else { + glyph_pbf.skip(); + } + } + + stack.insert(id, glyph); + } else { + fontstack_pbf.skip(); + } + } + } else { + glyphs_pbf.skip(); + } + } data.clear(); } @@ -57,11 +104,19 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set // We are implementing a blocking wait with futures: Every GlyphSet has a future that we are // waiting for until it is loaded. - std::vector> futures; + FontStack *stack = nullptr; + + std::vector> futures; futures.reserve(glyphRanges.size()); { std::lock_guard lock(mtx); - auto &rangeSets = stacks[fontStack]; + auto &rangeSets = ranges[fontStack]; + + auto stack_it = stacks.find(fontStack); + if (stack_it == stacks.end()) { + stack_it = stacks.emplace(fontStack, std::make_unique()).first; + } + stack = stack_it->second.get(); // Attempt to load the glyph range. If the GlyphSet already exists, we are getting back // the same shared_future. @@ -73,16 +128,16 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set // Now that we potentially created all GlyphSets, we are waiting for the results, one by one. // When we get a result (or the GlyphSet is aready loaded), we are attempting to parse the // GlyphSet. - for (std::shared_future &future : futures) { - future.get().parse(); + for (std::shared_future &future : futures) { + future.get().parse(*stack); } } -std::shared_future GlyphStore::loadGlyphRange(const std::string &name, std::map> &rangeSets, const GlyphRange range) { +std::shared_future GlyphStore::loadGlyphRange(const std::string &name, std::map> &rangeSets, const GlyphRange range) { auto range_it = rangeSets.find(range); if (range_it == rangeSets.end()) { // We don't have this glyph set yet for this font stack. - range_it = rangeSets.emplace(range, std::make_unique(name, range)).first; + range_it = rangeSets.emplace(range, std::make_unique(name, range)).first; } return range_it->second->getFuture();