Stash.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#define LLMR_GEOMETRY_GLYPH_ATLAS
|
||||
|
||||
#include <llmr/geometry/binpack.hpp>
|
||||
#include <llmr/text/glyph_store.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
@@ -30,7 +31,7 @@ public:
|
||||
|
||||
|
||||
Rect<uint16_t> addGlyph(uint64_t tile_id, const std::string& face_name,
|
||||
const VectorTileGlyph& glyph);
|
||||
const SDFGlyph& glyph);
|
||||
void removeGlyphs(uint64_t tile_id);
|
||||
void bind();
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <llmr/map/vector_tile_data.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/text/placement.hpp>
|
||||
#include <llmr/text/glyph_store.hpp>
|
||||
#include <llmr/text/glyph.hpp>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
@@ -23,6 +25,7 @@ private:
|
||||
bool obsolete() const;
|
||||
void parseGlyphs();
|
||||
void parseStyleLayers(const std::vector<LayerDescription>& layers);
|
||||
void addGlyph(uint64_t tileid, const std::string stackname, const std::string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
|
||||
std::unique_ptr<Bucket> createBucket(const BucketDescription& bucket_desc);
|
||||
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/text/types.hpp>
|
||||
#include <llmr/text/glyph.hpp>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -36,7 +37,7 @@ public:
|
||||
PlacementRange placementRange, float zoom);
|
||||
|
||||
void addFeature(const VectorTileFeature &feature,
|
||||
const IndexedFaces &faces,
|
||||
const GlyphPositions &face,
|
||||
const std::map<Value, Shaping> &shapings);
|
||||
|
||||
void drawGlyphs(TextShader &shader);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <llmr/text/glyph.hpp>
|
||||
#include <llmr/util/pbf.hpp>
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
@@ -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<uint32_t, GlyphMetrics> &getMetrics() const;
|
||||
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
|
||||
const Shaping getShaping(const std::string &string) const;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, std::string> bitmaps;
|
||||
std::map<uint32_t, GlyphMetrics> metrics;
|
||||
std::map<uint32_t, SDFGlyph> sdfs;
|
||||
mutable std::mutex mtx;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ public:
|
||||
|
||||
void addFeature(TextBucket &bucket, const std::vector<Coordinate> &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:
|
||||
|
||||
@@ -22,7 +22,7 @@ GlyphAtlas::~GlyphAtlas() {
|
||||
}
|
||||
|
||||
Rect<uint16_t> GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name,
|
||||
const VectorTileGlyph& glyph) {
|
||||
const SDFGlyph& glyph) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
// Use constant value for now.
|
||||
|
||||
@@ -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<std::string, const VectorTileFace> 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<uint16_t> 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<uint32_t, SDFGlyph> 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<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf);
|
||||
face.emplace(chr, Glyph{rect, sdf.metrics});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +197,7 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& laye
|
||||
// Create a copy!
|
||||
const FontStack &fontStack = glyphStore.getFontStack(bucket_desc.geometry.font);
|
||||
std::map<Value, Shaping> shaping;
|
||||
GlyphPositions face;
|
||||
|
||||
// Shape and place all labels.
|
||||
{
|
||||
@@ -219,33 +215,21 @@ std::unique_ptr<Bucket> 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<uint32_t, GlyphMetrics> 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<TextBucket> bucket = std::make_unique<TextBucket>(
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <llmr/style/style.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/text/placement.hpp>
|
||||
#include <llmr/text/glyph_store.hpp>
|
||||
#include <llmr/util/constants.hpp>
|
||||
|
||||
#include <llmr/util/math.hpp>
|
||||
@@ -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<Value, Shaping> &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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<std::mutex> 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<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
|
||||
@@ -21,6 +22,26 @@ const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return sdfs;
|
||||
}
|
||||
|
||||
const Shaping FontStack::getShaping(const std::string &string) const {
|
||||
std::lock_guard<std::mutex> 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();
|
||||
}
|
||||
|
||||
@@ -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<GlyphInstances> 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<Coordinate> &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<Coordinate> &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;
|
||||
|
||||
Reference in New Issue
Block a user