diff --git a/include/llmr/geometry/glyph_atlas.hpp b/include/llmr/geometry/glyph_atlas.hpp index 312572f5e..5bcd573f5 100644 --- a/include/llmr/geometry/glyph_atlas.hpp +++ b/include/llmr/geometry/glyph_atlas.hpp @@ -2,6 +2,7 @@ #define LLMR_GEOMETRY_GLYPH_ATLAS #include +#include #include #include @@ -30,7 +31,7 @@ public: Rect addGlyph(uint64_t tile_id, const std::string& face_name, - const VectorTileGlyph& glyph); + const SDFGlyph& glyph); void removeGlyphs(uint64_t tile_id); void bind(); diff --git a/include/llmr/map/tile_parser.hpp b/include/llmr/map/tile_parser.hpp index 644cc45b1..b4bccb67a 100644 --- a/include/llmr/map/tile_parser.hpp +++ b/include/llmr/map/tile_parser.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace llmr { @@ -23,6 +25,7 @@ private: bool obsolete() const; void parseGlyphs(); void parseStyleLayers(const std::vector& layers); + void addGlyph(uint64_t tileid, const std::string stackname, const std::string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); std::unique_ptr createBucket(const BucketDescription& bucket_desc); std::unique_ptr createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); std::unique_ptr createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); diff --git a/include/llmr/renderer/text_bucket.hpp b/include/llmr/renderer/text_bucket.hpp index 2b73aa74d..ad282bdd0 100644 --- a/include/llmr/renderer/text_bucket.hpp +++ b/include/llmr/renderer/text_bucket.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ public: PlacementRange placementRange, float zoom); void addFeature(const VectorTileFeature &feature, - const IndexedFaces &faces, + const GlyphPositions &face, const std::map &shapings); void drawGlyphs(TextShader &shader); diff --git a/include/llmr/text/glyph_store.hpp b/include/llmr/text/glyph_store.hpp index 90fa6d908..ea7c96dc4 100644 --- a/include/llmr/text/glyph_store.hpp +++ b/include/llmr/text/glyph_store.hpp @@ -4,7 +4,6 @@ #include #include - #include #include #include @@ -17,6 +16,8 @@ namespace llmr { class SDFGlyph { public: + uint32_t id = 0; + // A signed distance field of the glyph with a border of 3 pixels. std::string bitmap; @@ -26,13 +27,15 @@ public: class FontStack { public: - void insert(uint32_t id, const GlyphMetrics &glyphMetrics, const std::string &bitmap); - + void insert(uint32_t id, const SDFGlyph &glyph); const std::map &getMetrics() const; + const std::map &getSDFs() const; + const Shaping getShaping(const std::string &string) const; private: std::map bitmaps; std::map metrics; + std::map sdfs; mutable std::mutex mtx; }; diff --git a/include/llmr/text/placement.hpp b/include/llmr/text/placement.hpp index 8ec7df5e4..87c47345e 100644 --- a/include/llmr/text/placement.hpp +++ b/include/llmr/text/placement.hpp @@ -18,9 +18,9 @@ public: void addFeature(TextBucket &bucket, const std::vector &line, const BucketGeometryDescription &info, - const IndexedFaces &faces, + const GlyphPositions &face, const Shaping &shaping); - float measureText(const IndexedFaces &faces, + float measureText(const GlyphPositions &face, const Shaping &shaping); private: diff --git a/src/geometry/glyph_atlas.cpp b/src/geometry/glyph_atlas.cpp index 200d281f7..f4defac69 100644 --- a/src/geometry/glyph_atlas.cpp +++ b/src/geometry/glyph_atlas.cpp @@ -22,7 +22,7 @@ GlyphAtlas::~GlyphAtlas() { } Rect GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name, - const VectorTileGlyph& glyph) { + const SDFGlyph& glyph) { std::lock_guard lock(mtx); // Use constant value for now. diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 553c8c79a..2cd10049a 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -23,7 +23,6 @@ TileParser::TileParser(const std::string& data, VectorTileData& tile, const Styl glyphStore(glyphStore), spriteAtlas(spriteAtlas), placement(tile.id.z) { - parseGlyphs(); parseStyleLayers(style.layers); } @@ -31,17 +30,13 @@ bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; } -void TileParser::parseGlyphs() { - for (const std::pair pair : vector_data.faces) { - const std::string &name = pair.first; - const VectorTileFace &face = pair.second; - - GlyphPositions &glyphs = faces[name]; - for (const VectorTileGlyph &glyph : face.glyphs) { - const Rect rect = - glyphAtlas.addGlyph(tile.id.to_uint64(), name, glyph); - glyphs.emplace(glyph.id, Glyph{rect, glyph.metrics}); - } +void TileParser::addGlyph(uint64_t tileid, const std::string stackname, const std::string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face) { + std::map sdfs = fontStack.getSDFs(); + // Loop through all characters and add glyph to atlas, positions. + for (uint32_t chr : string) { + const SDFGlyph sdf = sdfs[chr]; + const Rect rect = glyphAtlas.addGlyph(tileid, stackname, sdf); + face.emplace(chr, Glyph{rect, sdf.metrics}); } } @@ -202,6 +197,7 @@ std::unique_ptr TileParser::createTextBucket(const VectorTileLayer& laye // Create a copy! const FontStack &fontStack = glyphStore.getFontStack(bucket_desc.geometry.font); std::map shaping; + GlyphPositions face; // Shape and place all labels. { @@ -219,33 +215,21 @@ std::unique_ptr TileParser::createTextBucket(const VectorTileLayer& laye continue; } - uint32_t i = 0; - uint32_t x = 0; const std::string string = toString(it_prop->second); - Shaping shapedGlyphs; - std::map metrics = fontStack.getMetrics(); - // fprintf(stderr, "%s\n", string.c_str()); - // TODO: Shape label - // Loop through all characters of this label and shape. - for (uint32_t chr : string) { - // Can we reuse GlyphPlacement here? First arg is a faces index. - GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0); - // No idea how to properly put together a shapedGlyphs vector. - shapedGylphs.push(glyph); - i++; - x += metrics[chr].advance; - } - // TODO: Place label + + // Shape labels. + const Shaping shaped = fontStack.getShaping(string); shaping.emplace(string, shaped); + + // Place labels. + addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, string, fontStack, glyphAtlas, face); } } - // Can faces here be a std::map of fontstacks? // It looks like nearly the same interface through the rest // of the stack. - std::unique_ptr bucket = std::make_unique( - tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement); - addBucketFeatures(bucket, layer, bucket_desc, faces, shaping); + addBucketFeatures(bucket, layer, bucket_desc, face, shaping); return std::move(bucket); } + diff --git a/src/renderer/text_bucket.cpp b/src/renderer/text_bucket.cpp index 310baf374..7e9633f4b 100644 --- a/src/renderer/text_bucket.cpp +++ b/src/renderer/text_bucket.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -99,7 +100,7 @@ void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, }; void TextBucket::addFeature(const VectorTileFeature &feature, - const IndexedFaces &faces, + const GlyphPositions &face, const std::map &shapings) { auto it_prop = feature.properties.find(geom_desc.field); if (it_prop == feature.properties.end()) { @@ -134,14 +135,14 @@ void TextBucket::addFeature(const VectorTileFeature &feature, while ((cmd = geometry.next(x, y)) != Geometry::end) { if (cmd == Geometry::move_to) { if (!line.empty()) { - placement.addFeature(*this, line, geom_desc, faces, shaping); + placement.addFeature(*this, line, geom_desc, face, shaping); line.clear(); } } line.emplace_back(x, y); } if (line.size()) { - placement.addFeature(*this, line, geom_desc, faces, shaping); + placement.addFeature(*this, line, geom_desc, face, shaping); } } diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index e2ebb97bd..88642907a 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -10,10 +10,11 @@ namespace llmr { -void FontStack::insert(uint32_t id, const GlyphMetrics &glyphMetrics, const std::string &bitmap) { +void FontStack::insert(uint32_t id, const SDFGlyph &glyph) { std::lock_guard lock(mtx); - metrics.emplace(id, glyphMetrics); - bitmaps.emplace(id, bitmap); + metrics.emplace(id, glyph.metrics); + bitmaps.emplace(id, glyph.bitmap); + sdfs.emplace(id, glyph); } const std::map &FontStack::getMetrics() const { @@ -21,6 +22,26 @@ const std::map &FontStack::getMetrics() const { return metrics; } +const std::map &FontStack::getSDFs() const { + std::lock_guard lock(mtx); + return sdfs; +} + +const Shaping FontStack::getShaping(const std::string &string) const { + std::lock_guard lock(mtx); + uint32_t i = 0; + uint32_t x = 0; + Shaping shaped; + // Loop through all characters of this label and shape. + for (uint32_t chr : string) { + GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0); + shaped.push_back(glyph); + i++; + x += metrics.find(chr)->second.advance; + } + return shaped; +} + GlyphPBF::GlyphPBF(const std::string &fontStack, GlyphRange glyphRange) : future(promise.get_future().share()) { @@ -30,6 +51,8 @@ GlyphPBF::GlyphPBF(const std::string &fontStack, GlyphRange glyphRange) // TODO: Find more reliable URL normalization function std::replace(url.begin(), url.end(), ' ', '+'); + fprintf(stderr, url.c_str()); + platform::request_http(url, [&](platform::Response *res) { if (res->code != 200) { // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. @@ -75,6 +98,7 @@ void GlyphPBF::parse(FontStack &stack) { while (glyph_pbf.next()) { if (glyph_pbf.tag == 1) { // id id = glyph_pbf.varint(); + glyph.id = glyph_pbf.varint(); } else if (glyph_pbf.tag == 2) { // bitmap glyph.bitmap = glyph_pbf.string(); } else if (glyph_pbf.tag == 3) { // width @@ -92,7 +116,7 @@ void GlyphPBF::parse(FontStack &stack) { } } - stack.insert(id, glyph.metrics, glyph.bitmap); + stack.insert(id, glyph); } else { fontstack_pbf.skip(); } diff --git a/src/text/placement.cpp b/src/text/placement.cpp index 237fd05d7..310f457cd 100644 --- a/src/text/placement.cpp +++ b/src/text/placement.cpp @@ -35,21 +35,12 @@ bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; } static const Glyph null_glyph; inline const Glyph &getGlyph(const GlyphPlacement &placed, - const IndexedFaces &faces) { - if (placed.face < faces.size()) { - const GlyphPositions &face = *faces[placed.face]; - if (&face) { - auto it = face.find(placed.glyph); - if (it != face.end()) { - return it->second; - } else { - fprintf(stderr, "glyph %d does not exist\n", placed.glyph); - } - } else { - fprintf(stderr, "face pointer is null\n"); - } + const GlyphPositions &face) { + auto it = face.find(placed.glyph); + if (it != face.end()) { + return it->second; } else { - fprintf(stderr, "face does not exist\n"); + fprintf(stderr, "glyph %d does not exist\n", placed.glyph); } return null_glyph; @@ -146,7 +137,7 @@ void getSegmentGlyphs(std::back_insert_iterator glyphs, } PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping, - const IndexedFaces &faces, float fontScale, + const GlyphPositions &face, float fontScale, bool horizontal, const std::vector &line, float maxAngleDelta, float rotate) { // The total text advance is the width of this label. @@ -167,7 +158,7 @@ PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping, const uint32_t buffer = 3; for (const GlyphPlacement &placed : shaping) { - const Glyph &glyph = getGlyph(placed, faces); + const Glyph &glyph = getGlyph(placed, face); if (!glyph) { // This glyph is empty and doesn't have any pixels that we'd need to // show. @@ -257,7 +248,7 @@ PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping, void Placement::addFeature(TextBucket& bucket, const std::vector &line, const BucketGeometryDescription &info, - const IndexedFaces &faces, + const GlyphPositions &face, const Shaping &shaping) { const bool horizontal = info.path == TextPathType::Horizontal; @@ -268,7 +259,7 @@ void Placement::addFeature(TextBucket& bucket, const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.size); - const float advance = measureText(faces, shaping); + const float advance = measureText(face, shaping); Anchors anchors; // fprintf(stderr, "adding feature with advance %f\n", advance); @@ -290,7 +281,7 @@ void Placement::addFeature(TextBucket& bucket, for (Anchor anchor : anchors) { PlacedGlyphs glyphs = - getGlyphs(anchor, advance, shaping, faces, fontScale, horizontal, + getGlyphs(anchor, advance, shaping, face, fontScale, horizontal, line, maxAngleDelta, rotate); PlacementProperty place = collision.place(glyphs, anchor, anchor.scale, maxPlacementScale, @@ -302,14 +293,14 @@ void Placement::addFeature(TextBucket& bucket, } } -float Placement::measureText(const IndexedFaces &faces, +float Placement::measureText(const GlyphPositions &face, const Shaping &shaping) { float advance = 0; // TODO: advance is not calculated correctly. we should instead use the // bounding box of the glyph placement. for (const GlyphPlacement &shape : shaping) { - advance += getGlyph(shape, faces).metrics.advance; + advance += getGlyph(shape, face).metrics.advance; } return advance;