summaryrefslogtreecommitdiff
path: root/src/mbgl
diff options
context:
space:
mode:
authorChris Loer <chris.loer@gmail.com>2017-03-31 14:53:18 -0700
committerChris Loer <chris.loer@mapbox.com>2017-04-04 11:33:12 -0700
commit5cdf838a387cae446dba500ac49a1c5524bf7949 (patch)
tree3b438034a7842c36a7804096785fca1a6ad6fa80 /src/mbgl
parent64beba3accb0f2088b2e01fad710f915c81d99c7 (diff)
downloadqtlocation-mapboxgl-5cdf838a387cae446dba500ac49a1c5524bf7949.tar.gz
[core] De-mutex GlyphAtlas and SpriteAtlas
- Expose glyph and icon information to workers via message interface. - Glyph/SpriteAtlas track which tiles have outstanding requests and send messages to them when glyphs/icons become available. - Remove obsolete "updateSymbolDependentTiles" pathway - Symbol preparation for a tile now depends on all glyphs becoming available before it can start. - Start tracking individual icons needed for a tile, although we don't do anything with the information yet. - Introduce typedef for GlyphID
Diffstat (limited to 'src/mbgl')
-rw-r--r--src/mbgl/layout/symbol_layout.cpp120
-rw-r--r--src/mbgl/layout/symbol_layout.hpp17
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp45
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp32
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.cpp8
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp5
-rw-r--r--src/mbgl/style/source_impl.cpp6
-rw-r--r--src/mbgl/style/source_impl.hpp4
-rw-r--r--src/mbgl/style/style.cpp8
-rw-r--r--src/mbgl/style/style.hpp1
-rw-r--r--src/mbgl/text/glyph.cpp2
-rw-r--r--src/mbgl/text/glyph.hpp19
-rw-r--r--src/mbgl/text/glyph_atlas.cpp245
-rw-r--r--src/mbgl/text/glyph_atlas.hpp68
-rw-r--r--src/mbgl/text/glyph_pbf.cpp3
-rw-r--r--src/mbgl/text/glyph_pbf.hpp6
-rw-r--r--src/mbgl/text/glyph_set.cpp275
-rw-r--r--src/mbgl/text/glyph_set.hpp31
-rw-r--r--src/mbgl/text/shaping.cpp288
-rw-r--r--src/mbgl/text/shaping.hpp14
-rw-r--r--src/mbgl/tile/geometry_tile.cpp39
-rw-r--r--src/mbgl/tile/geometry_tile.hpp15
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp133
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp21
-rw-r--r--src/mbgl/tile/tile.hpp1
-rw-r--r--src/mbgl/util/exclusive.hpp27
26 files changed, 804 insertions, 629 deletions
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index ad58f06311..978caabd70 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -7,10 +7,9 @@
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/get_anchors.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/text/shaping.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/utf.hpp>
#include <mbgl/util/token.hpp>
@@ -41,13 +40,15 @@ static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout)
SymbolLayout::SymbolLayout(const BucketParameters& parameters,
const std::vector<const Layer*>& layers,
const GeometryTileLayer& sourceLayer,
- SpriteAtlas& spriteAtlas_)
+ IconDependencies& iconDependencies,
+ uintptr_t _spriteAtlasMapIndex,
+ GlyphDependencies& glyphDependencies)
: sourceLayerName(sourceLayer.getName()),
bucketName(layers.at(0)->getID()),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
mode(parameters.mode),
- spriteAtlas(spriteAtlas_),
+ spriteAtlasMapIndex(_spriteAtlasMapIndex),
tileSize(util::tileSize * overscaling),
tilePixelRatio(float(util::EXTENT) / tileSize) {
@@ -95,7 +96,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
));
}
- // Determine and load glyph ranges
+ // Determine glyph dependencies
const size_t featureCount = sourceLayer.featureCount();
for (size_t i = 0; i < featureCount; ++i) {
auto feature = sourceLayer.getFeature(i);
@@ -141,9 +142,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
// Loop through all characters of this text and collect unique codepoints.
for (char16_t chr : *ft.text) {
- ranges.insert(getGlyphRange(chr));
+ glyphDependencies[layout.get<TextFont>()].insert(chr);
if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) {
- ranges.insert(getGlyphRange(verticalChr));
+ glyphDependencies[layout.get<TextFont>()].insert(verticalChr);
}
}
}
@@ -154,6 +155,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
icon = util::replaceTokens(icon, getValue);
}
ft.icon = icon;
+ iconDependencies.insert(*ft.icon);
}
if (ft.text || ft.icon) {
@@ -170,20 +172,7 @@ bool SymbolLayout::hasSymbolInstances() const {
return !symbolInstances.empty();
}
-bool SymbolLayout::canPrepare(GlyphAtlas& glyphAtlas) {
- if (has<TextField>(layout) && !layout.get<TextFont>().empty() && !glyphAtlas.hasGlyphRanges(layout.get<TextFont>(), ranges)) {
- return false;
- }
-
- if (has<IconImage>(layout) && !spriteAtlas.isLoaded()) {
- return false;
- }
-
- return true;
-}
-
-void SymbolLayout::prepare(uintptr_t tileUID,
- GlyphAtlas& glyphAtlas) {
+void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconAtlasMap& iconMap) {
float horizontalAlign = 0.5;
float verticalAlign = 0.5;
@@ -225,7 +214,6 @@ void SymbolLayout::prepare(uintptr_t tileUID,
layout.get<TextJustify>() == TextJustifyType::Left ? 0 :
0.5;
- auto glyphSet = glyphAtlas.getGlyphSet(layout.get<TextFont>());
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
@@ -240,59 +228,61 @@ void SymbolLayout::prepare(uintptr_t tileUID,
// if feature has text, shape the text
if (feature.text) {
- auto getShaping = [&] (const std::u16string& text, WritingModeType writingMode) {
- const float oneEm = 24.0f;
- const Shaping result = glyphSet->getShaping(
- /* string */ text,
- /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
- layout.get<TextMaxWidth>() * oneEm : 0,
- /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
- /* horizontalAlign */ horizontalAlign,
- /* verticalAlign */ verticalAlign,
- /* justify */ justify,
- /* spacing: ems */ layout.get<TextLetterSpacing>() * oneEm,
- /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
- /* verticalHeight */ oneEm,
- /* writingMode */ writingMode,
- /* bidirectional algorithm object */ bidi);
-
- // Add the glyphs we need for this label to the glyph atlas.
- if (result) {
- glyphAtlas.addGlyphs(tileUID, text, layout.get<TextFont>(), glyphSet, face);
+ auto glyphPositions = glyphs.find(layout.get<TextFont>());
+ if (glyphPositions != glyphs.end()) { // If there are no glyphs available for this feature, skip shaping
+ auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) {
+ const float oneEm = 24.0f;
+ const Shaping result = getShaping(
+ /* string */ text,
+ /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
+ layout.get<TextMaxWidth>() * oneEm : 0,
+ /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
+ /* horizontalAlign */ horizontalAlign,
+ /* verticalAlign */ verticalAlign,
+ /* justify */ justify,
+ /* spacing: ems */ layout.get<TextLetterSpacing>() * oneEm,
+ /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
+ /* verticalHeight */ oneEm,
+ /* writingMode */ writingMode,
+ /* bidirectional algorithm object */ bidi,
+ /* glyphs */ glyphPositions->second);
+
+ return result;
+ };
+
+ shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal);
+
+ if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) {
+ shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical);
}
-
- return result;
- };
-
- shapedTextOrientations.first = getShaping(*feature.text, WritingModeType::Horizontal);
-
- if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) {
- shapedTextOrientations.second = getShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical);
}
}
// if feature has icon, get sprite atlas position
if (feature.icon) {
- auto image = spriteAtlas.getIcon(*feature.icon);
- if (image) {
- shapedIcon = shapeIcon(*image,
- layout.evaluate<IconOffset>(zoom, feature),
- layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
- assert((*image).spriteImage);
- if ((*image).spriteImage->sdf) {
- sdfIcons = true;
- }
- if ((*image).relativePixelRatio != 1.0f) {
- iconsNeedLinear = true;
- } else if (layout.get<IconRotate>().constantOr(1) != 0) {
- iconsNeedLinear = true;
+ auto icons = iconMap.find(spriteAtlasMapIndex);
+ if (icons != iconMap.end()) {
+ auto image = icons->second.find(*feature.icon);
+ if (image != icons->second.end()) {
+ shapedIcon = shapeIcon(image->second,
+ layout.evaluate<IconOffset>(zoom, feature),
+ layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
+ if (image->second.sdf) {
+ sdfIcons = true;
+ }
+ if (image->second.relativePixelRatio != 1.0f) {
+ iconsNeedLinear = true;
+ } else if (layout.get<IconRotate>().constantOr(1) != 0) {
+ iconsNeedLinear = true;
+ }
}
}
}
// if either shapedText or icon position is present, add the feature
if (shapedTextOrientations.first || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, face);
+ auto glyphPositionsIt = glyphs.find(layout.get<TextFont>());
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionsIt == glyphs.end() ? GlyphPositions() : glyphPositionsIt->second);
}
feature.geometry.clear();
@@ -305,7 +295,7 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
const PositionedIcon& shapedIcon,
- const GlyphPositions& face) {
+ const GlyphPositions& glyphs) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
@@ -351,7 +341,7 @@ void SymbolLayout::addFeature(const std::size_t index,
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), addToBuffers, symbolInstances.size(),
textBoxScale, textPadding, textPlacement,
iconBoxScale, iconPadding, iconPlacement,
- face, indexedFeature, index);
+ glyphs, indexedFeature, index);
};
const auto& type = feature.getType();
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index bd72d3cd3f..d79ccb3273 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -16,8 +16,6 @@ namespace mbgl {
class GeometryTileLayer;
class CollisionTile;
-class SpriteAtlas;
-class GlyphAtlas;
class SymbolBucket;
class Anchor;
@@ -32,12 +30,11 @@ public:
SymbolLayout(const style::BucketParameters&,
const std::vector<const style::Layer*>&,
const GeometryTileLayer&,
- SpriteAtlas&);
+ IconDependencies&,
+ uintptr_t,
+ GlyphDependencies&);
- bool canPrepare(GlyphAtlas&);
-
- void prepare(uintptr_t tileUID,
- GlyphAtlas&);
+ void prepare(const GlyphPositionMap& glyphs, const IconAtlasMap& icons);
std::unique_ptr<SymbolBucket> place(CollisionTile&);
@@ -45,7 +42,6 @@ public:
enum State {
Pending, // Waiting for the necessary glyphs or icons to be available.
- Prepared, // The potential positions of text and icons have been determined.
Placed // The final positions have been determined, taking into account prior layers.
};
@@ -80,8 +76,8 @@ private:
style::SymbolLayoutProperties::PossiblyEvaluated layout;
float textMaxSize;
-
- SpriteAtlas& spriteAtlas;
+
+ uintptr_t spriteAtlasMapIndex; // Actually a pointer to the SpriteAtlas for this symbol's layer, but don't use it from worker threads!
const uint32_t tileSize;
const float tilePixelRatio;
@@ -89,7 +85,6 @@ private:
bool sdfIcons = false;
bool iconsNeedLinear = false;
- GlyphRangeSet ranges;
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
index 2bf8a3095a..bdccf289df 100644
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ b/src/mbgl/sprite/sprite_atlas.cpp
@@ -28,11 +28,13 @@ struct SpriteAtlas::Loader {
};
SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_,
- std::shared_ptr<const SpriteImage> image_,
+ std::shared_ptr<const SpriteImage> spriteImage,
Size size_, float pixelRatio)
: pos(std::move(rect_)),
- spriteImage(std::move(image_)),
- relativePixelRatio(spriteImage->pixelRatio / pixelRatio) {
+ sdf(spriteImage->sdf),
+ relativePixelRatio(spriteImage->pixelRatio / pixelRatio),
+ width(spriteImage->getWidth()),
+ height(spriteImage->getHeight()) {
const float padding = 1;
@@ -105,6 +107,10 @@ void SpriteAtlas::emitSpriteLoadedIfComplete() {
loaded = true;
setSprites(result.get<Sprites>());
observer->onSpriteLoaded();
+ for (auto requestor : requestors) {
+ requestor->onIconsAvailable(this, buildIconMap());
+ }
+ requestors.clear();
} else {
observer->onSpriteError(result.get<std::exception_ptr>());
}
@@ -119,20 +125,17 @@ void SpriteAtlas::dumpDebugLogs() const {
}
void SpriteAtlas::setSprites(const Sprites& newSprites) {
- std::lock_guard<std::mutex> lock(mutex);
for (const auto& pair : newSprites) {
_setSprite(pair.first, pair.second);
}
}
void SpriteAtlas::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
- std::lock_guard<std::mutex> lock(mutex);
_setSprite(name, sprite);
}
void SpriteAtlas::removeSprite(const std::string& name) {
- std::lock_guard<std::mutex> lock(mutex);
-
+ icons.clear();
auto it = entries.find(name);
if (it == entries.end()) {
return;
@@ -153,6 +156,7 @@ void SpriteAtlas::removeSprite(const std::string& name) {
void SpriteAtlas::_setSprite(const std::string& name,
const std::shared_ptr<const SpriteImage>& sprite) {
+ icons.clear();
if (!sprite->image.valid()) {
Log::Warning(Event::Sprite, "invalid sprite image '%s'", name.c_str());
return;
@@ -184,7 +188,6 @@ void SpriteAtlas::_setSprite(const std::string& name,
}
std::shared_ptr<const SpriteImage> SpriteAtlas::getSprite(const std::string& name) {
- std::lock_guard<std::mutex> lock(mutex);
const auto it = entries.find(name);
if (it != entries.end()) {
return it->second.spriteImage;
@@ -196,6 +199,18 @@ std::shared_ptr<const SpriteImage> SpriteAtlas::getSprite(const std::string& nam
}
}
+void SpriteAtlas::getIcons(IconRequestor& requestor) {
+ if (isLoaded()) {
+ requestor.onIconsAvailable(this, buildIconMap());
+ } else {
+ requestors.insert(&requestor);
+ }
+}
+
+void SpriteAtlas::removeRequestor(IconRequestor& requestor) {
+ requestors.erase(&requestor);
+}
+
optional<SpriteAtlasElement> SpriteAtlas::getIcon(const std::string& name) {
return getImage(name, &Entry::iconRect);
}
@@ -206,7 +221,6 @@ optional<SpriteAtlasElement> SpriteAtlas::getPattern(const std::string& name) {
optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name,
optional<Rect<uint16_t>> Entry::*entryRect) {
- std::lock_guard<std::mutex> lock(mutex);
auto it = entries.find(name);
if (it == entries.end()) {
@@ -219,6 +233,7 @@ optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name,
Entry& entry = it->second;
if (entry.*entryRect) {
+ assert(entry.spriteImage.get());
return SpriteAtlasElement {
*(entry.*entryRect),
entry.spriteImage,
@@ -285,6 +300,18 @@ void SpriteAtlas::copy(const Entry& entry, optional<Rect<uint16_t>> Entry::*entr
dirty = true;
}
+IconMap SpriteAtlas::buildIconMap() {
+ if (icons.empty()) {
+ for (auto entry : entries) {
+ icons.emplace(std::piecewise_construct,
+ std::forward_as_tuple(entry.first),
+ std::forward_as_tuple(*getIcon(entry.first)));
+
+ }
+ }
+ return icons;
+}
+
void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
if (!texture) {
texture = context.createTexture(image, unit);
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
index c7b266376b..3c37f57708 100644
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ b/src/mbgl/sprite/sprite_atlas.hpp
@@ -6,10 +6,9 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/sprite/sprite_image.hpp>
-#include <atomic>
#include <string>
#include <map>
-#include <mutex>
+#include <set>
#include <unordered_map>
#include <array>
#include <memory>
@@ -28,12 +27,26 @@ public:
SpriteAtlasElement(Rect<uint16_t>, std::shared_ptr<const SpriteImage>, Size size, float pixelRatio);
Rect<uint16_t> pos;
- std::shared_ptr<const SpriteImage> spriteImage;
+ bool sdf;
float relativePixelRatio;
std::array<float, 2> size;
std::array<float, 2> tl;
std::array<float, 2> br;
+ float width;
+ float height;
+};
+
+class SpriteAtlas;
+
+typedef std::map<std::string,SpriteAtlasElement> IconMap;
+typedef std::set<std::string> IconDependencies;
+typedef std::map<uintptr_t,IconMap> IconAtlasMap;
+typedef std::map<SpriteAtlas*,IconDependencies> IconDependencyMap;
+
+class IconRequestor {
+public:
+ virtual void onIconsAvailable(SpriteAtlas*, IconMap) = 0;
};
class SpriteAtlas : public util::noncopyable {
@@ -55,8 +68,11 @@ public:
void setSprite(const std::string&, std::shared_ptr<const SpriteImage>);
void removeSprite(const std::string&);
+
+ std::shared_ptr<const SpriteImage> getSprite(const std::string& name);
- std::shared_ptr<const SpriteImage> getSprite(const std::string&);
+ void getIcons(IconRequestor& requestor);
+ void removeRequestor(IconRequestor& requestor);
optional<SpriteAtlasElement> getIcon(const std::string& name);
optional<SpriteAtlasElement> getPattern(const std::string& name);
@@ -105,13 +121,17 @@ private:
optional<SpriteAtlasElement> getImage(const std::string& name, optional<Rect<uint16_t>> Entry::*rect);
void copy(const Entry&, optional<Rect<uint16_t>> Entry::*rect);
+
+ IconMap buildIconMap();
- std::mutex mutex;
std::unordered_map<std::string, Entry> entries;
BinPack<uint16_t> bin;
PremultipliedImage image;
mbgl::optional<gl::Texture> texture;
- std::atomic<bool> dirty;
+ bool dirty;
+
+ std::set<IconRequestor*> requestors;
+ IconMap icons;
};
} // namespace mbgl
diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp
index e4557c953f..c637770c04 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.cpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.cpp
@@ -36,11 +36,15 @@ std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(const BucketParameters&,
std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(const BucketParameters& parameters,
const std::vector<const Layer*>& group,
- const GeometryTileLayer& layer) const {
+ const GeometryTileLayer& layer,
+ GlyphDependencies& glyphDependencies,
+ IconDependencyMap& iconDependencyMap) const {
return std::make_unique<SymbolLayout>(parameters,
group,
layer,
- *spriteAtlas);
+ iconDependencyMap[spriteAtlas],
+ (uintptr_t)spriteAtlas,
+ glyphDependencies);
}
IconPaintProperties::Evaluated SymbolLayer::Impl::iconPaintProperties() const {
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index 0c4b74e833..db20989f01 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -1,13 +1,14 @@
#pragma once
+#include <mbgl/text/glyph.hpp>
#include <mbgl/util/variant.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
namespace mbgl {
-class SpriteAtlas;
class SymbolLayout;
namespace style {
@@ -67,7 +68,7 @@ public:
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override;
std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const Layer*>&,
- const GeometryTileLayer&) const;
+ const GeometryTileLayer&, GlyphDependencies&, IconDependencyMap&) const;
IconPaintProperties::Evaluated iconPaintProperties() const;
TextPaintProperties::Evaluated textPaintProperties() const;
diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp
index 356d420e19..9fabc54f7d 100644
--- a/src/mbgl/style/source_impl.cpp
+++ b/src/mbgl/style/source_impl.cpp
@@ -191,12 +191,6 @@ void Source::Impl::removeTiles() {
}
}
-void Source::Impl::updateSymbolDependentTiles() {
- for (auto& pair : tiles) {
- pair.second->symbolDependenciesChanged();
- }
-}
-
void Source::Impl::reloadTiles() {
cache.clear();
diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp
index b4941dc638..e8ba54b2b6 100644
--- a/src/mbgl/style/source_impl.hpp
+++ b/src/mbgl/style/source_impl.hpp
@@ -48,10 +48,6 @@ public:
// trigger re-placement of existing complete tiles.
void updateTiles(const UpdateParameters&);
- // Called when icons or glyphs are loaded. Triggers further processing of tiles which
- // were waiting on such dependencies.
- void updateSymbolDependentTiles();
-
// Removes all tiles (by putting them into the cache).
void removeTiles();
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 5f9983ae94..9640988402 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -274,12 +274,6 @@ void Style::updateTiles(const UpdateParameters& parameters) {
}
}
-void Style::updateSymbolDependentTiles() {
- for (const auto& source : sources) {
- source->baseImpl->updateSymbolDependentTiles();
- }
-}
-
void Style::relayout() {
for (const auto& sourceID : updateBatch.sourceIDs) {
Source* source = getSource(sourceID);
@@ -575,7 +569,6 @@ void Style::setObserver(style::Observer* observer_) {
void Style::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) {
observer->onGlyphsLoaded(fontStack, glyphRange);
- updateSymbolDependentTiles();
}
void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
@@ -626,7 +619,6 @@ void Style::onTileError(Source& source, const OverscaledTileID& tileID, std::exc
void Style::onSpriteLoaded() {
observer->onSpriteLoaded();
observer->onUpdate(Update::Repaint); // For *-pattern properties.
- updateSymbolDependentTiles();
}
void Style::onSpriteError(std::exception_ptr error) {
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 0c65129422..817cab5fd5 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -128,7 +128,6 @@ private:
std::vector<std::unique_ptr<Layer>>::const_iterator findLayer(const std::string& layerID) const;
void reloadLayerSource(Layer&);
- void updateSymbolDependentTiles();
// GlyphStoreObserver implementation.
void onGlyphsLoaded(const FontStack&, const GlyphRange&) override;
diff --git a/src/mbgl/text/glyph.cpp b/src/mbgl/text/glyph.cpp
index 29929b73e6..74863d7435 100644
--- a/src/mbgl/text/glyph.cpp
+++ b/src/mbgl/text/glyph.cpp
@@ -3,7 +3,7 @@
namespace mbgl {
// Note: this only works for the BMP
-GlyphRange getGlyphRange(char16_t glyph) {
+GlyphRange getGlyphRange(GlyphID glyph) {
unsigned start = (glyph/256) * 256;
unsigned end = (start + 255);
if (start > 65280) start = 65280;
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 175a36f0fa..6aa4e11b1c 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/rect.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/image.hpp>
@@ -12,8 +13,11 @@
namespace mbgl {
+typedef char16_t GlyphID;
+typedef std::set<GlyphID> GlyphIDs;
+
// Note: this only works for the BMP
-GlyphRange getGlyphRange(char16_t glyph);
+GlyphRange getGlyphRange(GlyphID glyph);
struct GlyphMetrics {
explicit operator bool() const {
@@ -50,14 +54,14 @@ struct Glyph {
const GlyphMetrics metrics;
};
-typedef std::map<uint32_t, Glyph> GlyphPositions;
+typedef std::map<GlyphID, Glyph> GlyphPositions;
class PositionedGlyph {
public:
- explicit PositionedGlyph(uint32_t glyph_, float x_, float y_, float angle_)
+ explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, float angle_)
: glyph(glyph_), x(x_), y(y_), angle(angle_) {}
- uint32_t glyph = 0;
+ GlyphID glyph = 0;
float x = 0;
float y = 0;
float angle = 0;
@@ -86,7 +90,7 @@ public:
// also need to be reencoded.
static constexpr const uint8_t borderSize = 3;
- uint32_t id = 0;
+ GlyphID id = 0;
// A signed distance field of the glyph with a border (see above).
AlphaImage bitmap;
@@ -121,4 +125,9 @@ constexpr WritingModeType operator~(WritingModeType value) {
return WritingModeType(~mbgl::underlying_type(value));
}
+typedef std::map<FontStack,GlyphIDs> GlyphDependencies;
+typedef std::map<FontStack,GlyphRangeSet> GlyphRangeDependencies;
+typedef std::map<FontStack,GlyphPositions> GlyphPositionMap;
+
+
} // end namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp
index 1b3f7518b5..ade2734ca9 100644
--- a/src/mbgl/text/glyph_atlas.cpp
+++ b/src/mbgl/text/glyph_atlas.cpp
@@ -22,81 +22,140 @@ GlyphAtlas::GlyphAtlas(const Size size, FileSource& fileSource_)
GlyphAtlas::~GlyphAtlas() = default;
-void GlyphAtlas::requestGlyphRange(const FontStack& fontStack, const GlyphRange& range) {
- std::lock_guard<std::mutex> lock(mutex);
- auto& rangeSets = entries[fontStack].ranges;
-
- const auto& rangeSetsIt = rangeSets.find(range);
- if (rangeSetsIt != rangeSets.end()) {
- return;
+GlyphSet& GlyphAtlas::getGlyphSet(const FontStack& fontStack) {
+ return entries[fontStack].glyphSet;
+}
+
+bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& ranges) const {
+ for (const auto& range : ranges) {
+ if (!hasGlyphRange(fontStack,range)) {
+ return false;
+ }
}
-
- rangeSets.emplace(std::piecewise_construct,
- std::forward_as_tuple(range),
- std::forward_as_tuple(this, fontStack, range, observer, fileSource));
+ return true;
+}
+
+bool rangeIsParsed(const std::map<GlyphRange, GlyphPBF>& ranges, const GlyphRange& range) {
+ auto rangeIt = ranges.find(range);
+ if (rangeIt == ranges.end())
+ return false;
+
+ return rangeIt->second.isParsed();
}
+
+bool GlyphAtlas::hasGlyphRange(const FontStack& fontStack, const GlyphRange& range) const {
+ auto entry = entries.find(fontStack);
+ if (entry == entries.end())
+ return false;
+
+ return rangeIsParsed(entry->second.ranges, range);
+}
+
+void GlyphAtlas::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) {
+ auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
+
+ // Figure out which glyph ranges need to be fetched. For each range that does need to
+ // be fetched, record an entry mapping the requestor to a shared pointer containing the
+ // dependencies. When the shared pointer becomes unique, we know that all the dependencies
+ // for that requestor have been fetched, and can notify it of completion.
+ for (const auto& dependency : *dependencies) {
+ const FontStack& fontStack = dependency.first;
+ const GlyphIDs& glyphIDs = dependency.second;
+
+ Entry& entry = entries[fontStack];
+
+ GlyphRangeSet processedRanges;
+ for (const auto& glyphID : glyphIDs) {
+ GlyphRange range = getGlyphRange(glyphID);
+ if (processedRanges.find(range) == processedRanges.end() && !rangeIsParsed(entry.ranges, range)) {
+ if (entry.ranges.find(range) == entry.ranges.end()) {
+ entry.ranges.emplace(std::piecewise_construct,
+ std::forward_as_tuple(range),
+ std::forward_as_tuple(this, fontStack, range, this, fileSource));
+ }
-bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& glyphRanges) {
- if (glyphRanges.empty()) {
- return true;
+ entry.ranges.find(range)->second.requestors[&requestor] = dependencies;
+ }
+ processedRanges.insert(range);
+ }
}
- std::lock_guard<std::mutex> lock(mutex);
- const auto& rangeSets = entries[fontStack].ranges;
+ // If the shared dependencies pointer is already unique, then all dependent glyph ranges
+ // have already been loaded. Send a notification immediately.
+ if (dependencies.unique()) {
+ addGlyphs(requestor, *dependencies);
+ }
+}
- bool hasRanges = true;
- for (const auto& range : glyphRanges) {
- const auto& rangeSetsIt = rangeSets.find(range);
- if (rangeSetsIt == rangeSets.end()) {
- // Push the request to the MapThread, so we can easly cancel
- // if it is still pending when we destroy this object.
- workQueue.push(std::bind(&GlyphAtlas::requestGlyphRange, this, fontStack, range));
+void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) {
+ observer = observer_;
+}
+
+void GlyphAtlas::onGlyphsError(const FontStack& fontStack, const GlyphRange& range, std::exception_ptr err) {
+ if (observer) {
+ observer->onGlyphsError(fontStack, range, err);
+ }
+}
- hasRanges = false;
- continue;
- }
+void GlyphAtlas::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& range) {
+ Entry& entry = entries[fontStack];
- if (!rangeSetsIt->second.isParsed()) {
- hasRanges = false;
+ auto it = entry.ranges.find(range);
+ if (it != entry.ranges.end()) {
+ for (auto& pair : it->second.requestors) {
+ GlyphRequestor& requestor = *pair.first;
+ const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
+ if (dependencies.unique()) {
+ addGlyphs(requestor, *dependencies);
+ }
}
+ it->second.requestors.clear();
}
- return hasRanges;
+ if (observer) {
+ observer->onGlyphsLoaded(fontStack, range);
+ }
}
-util::exclusive<GlyphSet> GlyphAtlas::getGlyphSet(const FontStack& fontStack) {
- auto lock = std::make_unique<std::lock_guard<std::mutex>>(mutex);
- return { &entries[fontStack].glyphSet, std::move(lock) };
-}
+void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) {
+ GlyphPositionMap glyphPositions;
+ GlyphRangeSet loadedRanges;
-void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) {
- observer = observer_;
-}
+ for (const auto& dependency : glyphDependencies) {
+ const FontStack& fontStack = dependency.first;
+ const GlyphIDs& glyphIDs = dependency.second;
-void GlyphAtlas::addGlyphs(uintptr_t tileUID,
- const std::u16string& text,
- const FontStack& fontStack,
- const util::exclusive<GlyphSet>& glyphSet,
- GlyphPositions& face)
-{
- const std::map<uint32_t, SDFGlyph>& sdfs = glyphSet->getSDFs();
+ GlyphPositions& positions = glyphPositions[fontStack];
+ Entry& entry = entries[fontStack];
+ const auto& sdfs = entry.glyphSet.getSDFs();
- for (char16_t chr : text)
- {
- auto sdf_it = sdfs.find(chr);
- if (sdf_it == sdfs.end()) {
- continue;
- }
+ for (const auto& glyphID : glyphIDs) {
+ loadedRanges.insert(getGlyphRange(glyphID));
+ auto it = sdfs.find(glyphID);
+ if (it == sdfs.end())
+ continue;
+
+ addGlyph(requestor, fontStack, it->second);
- const SDFGlyph& sdf = sdf_it->second;
- Rect<uint16_t> rect = addGlyph(tileUID, fontStack, sdf);
- face.emplace(chr, Glyph{rect, sdf.metrics});
+ // It's possible to have an SDF without a valid position (if the SDF was malformed).
+ // We indicate this case with Rect<uint16_t>(0,0,0,0).
+ auto glyphRect = entry.glyphValues.find(glyphID);
+ const Rect<uint16_t> rect = glyphRect == entry.glyphValues.end()
+ ? Rect<uint16_t>(0,0,0,0)
+ : glyphRect->second.rect;
+
+ positions.emplace(std::piecewise_construct,
+ std::forward_as_tuple(glyphID),
+ std::forward_as_tuple(rect, it->second.metrics));
+ }
}
+
+ requestor.onGlyphsAvailable(glyphPositions, loadedRanges);
}
-Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
- const FontStack& fontStack,
- const SDFGlyph& glyph)
+void GlyphAtlas::addGlyph(GlyphRequestor& requestor,
+ const FontStack& fontStack,
+ const SDFGlyph& glyph)
{
std::map<uint32_t, GlyphValue>& face = entries[fontStack].glyphValues;
auto it = face.find(glyph.id);
@@ -104,15 +163,15 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
// The glyph is already in this texture.
if (it != face.end()) {
GlyphValue& value = it->second;
- value.ids.insert(tileUID);
- return value.rect;
+ value.ids.insert(&requestor);
+ return;
}
// Guard against glyphs that are too large, or that we don't need to place into the atlas since
// they don't have any pixels.
if (glyph.metrics.width == 0 || glyph.metrics.width >= 256 ||
glyph.metrics.height == 0 || glyph.metrics.height >= 256) {
- return Rect<uint16_t>{ 0, 0, 0, 0 };
+ return;
}
// Add a 1px border around every image.
@@ -129,53 +188,59 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
Rect<uint16_t> rect = bin.allocate(width, height);
if (rect.w == 0) {
Log::Error(Event::OpenGL, "glyph bitmap overflow");
- return rect;
+ return;
}
- face.emplace(glyph.id, GlyphValue { rect, tileUID });
+ face.emplace(glyph.id, GlyphValue { rect, &requestor });
AlphaImage::copy(glyph.bitmap, image, { 0, 0 }, { rect.x + padding, rect.y + padding }, glyph.bitmap.size);
dirty = true;
-
- return rect;
}
-void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
- std::lock_guard<std::mutex> lock(mutex);
+void GlyphAtlas::removeGlyphValues(GlyphRequestor& requestor, std::map<uint32_t, GlyphValue>& face) {
+ for (auto it = face.begin(); it != face.end(); /* we advance in the body */) {
+ GlyphValue& value = it->second;
+ value.ids.erase(&requestor);
- for (auto& entry : entries) {
- std::map<uint32_t, GlyphValue>& face = entry.second.glyphValues;
- for (auto it = face.begin(); it != face.end(); /* we advance in the body */) {
- GlyphValue& value = it->second;
- value.ids.erase(tileUID);
-
- if (value.ids.empty()) {
- const Rect<uint16_t>& rect = value.rect;
-
- // Clear out the bitmap.
- uint8_t *target = image.data.get();
- for (uint32_t y = 0; y < rect.h; y++) {
- uint32_t y1 = image.size.width * (rect.y + y) + rect.x;
- for (uint32_t x = 0; x < rect.w; x++) {
- target[y1 + x] = 0;
- }
+ if (value.ids.empty()) {
+ const Rect<uint16_t>& rect = value.rect;
+
+ // Clear out the bitmap.
+ uint8_t *target = image.data.get();
+ for (uint32_t y = 0; y < rect.h; y++) {
+ uint32_t y1 = image.size.width * (rect.y + y) + rect.x;
+ for (uint32_t x = 0; x < rect.w; x++) {
+ target[y1 + x] = 0;
}
+ }
- bin.release(rect);
+ bin.release(rect);
- // Make sure to post-increment the iterator: This will return the
- // current iterator, but will go to the next position before we
- // erase the element from the map. That way, the iterator stays
- // valid.
- face.erase(it++);
- } else {
- ++it;
- }
+ // Make sure to post-increment the iterator: This will return the
+ // current iterator, but will go to the next position before we
+ // erase the element from the map. That way, the iterator stays
+ // valid.
+ face.erase(it++);
+ } else {
+ ++it;
}
}
}
+void GlyphAtlas::removePendingRanges(mbgl::GlyphRequestor &requestor, std::map<GlyphRange, GlyphPBF> &ranges) {
+ for (auto it = ranges.begin(); it != ranges.end(); it++) {
+ it->second.requestors.erase(&requestor);
+ }
+}
+
+void GlyphAtlas::removeGlyphs(GlyphRequestor& requestor) {
+ for (auto& entry : entries) {
+ removeGlyphValues(requestor, entry.second.glyphValues);
+ removePendingRanges(requestor, entry.second.ranges);
+ }
+}
+
Size GlyphAtlas::getSize() const {
return image.size;
}
@@ -186,7 +251,7 @@ void GlyphAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
} else if (dirty) {
context.updateTexture(*texture, image, unit);
}
-
+
dirty = false;
}
diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp
index 8267630096..120f19accc 100644
--- a/src/mbgl/text/glyph_atlas.hpp
+++ b/src/mbgl/text/glyph_atlas.hpp
@@ -1,46 +1,55 @@
#pragma once
#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_atlas_observer.hpp>
+#include <mbgl/text/glyph_range.hpp>
#include <mbgl/text/glyph_set.hpp>
#include <mbgl/geometry/binpack.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/font_stack.hpp>
-#include <mbgl/util/exclusive.hpp>
#include <mbgl/util/work_queue.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/gl/texture.hpp>
#include <mbgl/gl/object.hpp>
-#include <atomic>
#include <string>
#include <unordered_set>
#include <unordered_map>
-#include <mutex>
+
+class GlyphAtlasTest;
namespace mbgl {
class FileSource;
class GlyphPBF;
-class GlyphAtlasObserver;
namespace gl {
class Context;
} // namespace gl
-class GlyphAtlas : public util::noncopyable {
+class GlyphRequestor {
+public:
+ virtual void onGlyphsAvailable(GlyphPositionMap, GlyphRangeSet) = 0;
+};
+
+class GlyphAtlas : public util::noncopyable, public GlyphAtlasObserver {
public:
GlyphAtlas(Size, FileSource&);
~GlyphAtlas();
- util::exclusive<GlyphSet> getGlyphSet(const FontStack&);
-
- // Returns true if the set of GlyphRanges are available and parsed or false
- // if they are not. For the missing ranges, a request on the FileSource is
- // made and when the glyph if finally parsed, it gets added to the respective
- // GlyphSet and a signal is emitted to notify the observers. This method
- // can be called from any thread.
- bool hasGlyphRanges(const FontStack&, const GlyphRangeSet&);
+ GlyphSet& getGlyphSet(const FontStack&);
+
+ // Workers send a `getGlyphs` message to the main thread once they have determined
+ // which glyphs they will need. Invoking this method will increment reference
+ // counts for all the glyphs in `GlyphDependencies`. If all glyphs are already
+ // locally available, the observer will be notified that the glyphs are available
+ // immediately. Otherwise, a request on the FileSource is made, and when all glyphs
+ // are parsed and added to the atlas, the observer will be notified.
+ // Workers are given a copied 'GlyphPositions' map to use for placing their glyphs.
+ // The positions specified in this object are guaranteed to be
+ // valid for the lifetime of the tile.
+ void getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphs);
void setURL(const std::string &url) {
glyphURL = url;
@@ -52,12 +61,7 @@ public:
void setObserver(GlyphAtlasObserver* observer);
- void addGlyphs(uintptr_t tileUID,
- const std::u16string& text,
- const FontStack&,
- const util::exclusive<GlyphSet>&,
- GlyphPositions&);
- void removeGlyphs(uintptr_t tileUID);
+ void removeGlyphs(GlyphRequestor&);
// Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind(gl::Context&, gl::TextureUnit unit);
@@ -67,22 +71,29 @@ public:
void upload(gl::Context&, gl::TextureUnit unit);
Size getSize() const;
+
+ virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&);
+ virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr);
+
+ friend class ::GlyphAtlasTest;
private:
- void requestGlyphRange(const FontStack&, const GlyphRange&);
+ void addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies);
- Rect<uint16_t> addGlyph(uintptr_t tileID,
- const FontStack&,
- const SDFGlyph&);
+ // Only used by GlyphAtlasTest
+ bool hasGlyphRanges(const FontStack&, const GlyphRangeSet& ranges) const;
+ bool hasGlyphRange(const FontStack&, const GlyphRange& range) const;
+ void addGlyph(GlyphRequestor& requestor, const FontStack&, const SDFGlyph&);
+
FileSource& fileSource;
std::string glyphURL;
struct GlyphValue {
- GlyphValue(Rect<uint16_t> rect_, uintptr_t id)
+ GlyphValue(Rect<uint16_t> rect_, GlyphRequestor* id)
: rect(std::move(rect_)), ids({ id }) {}
Rect<uint16_t> rect;
- std::unordered_set<uintptr_t> ids;
+ std::unordered_set<GlyphRequestor*> ids;
};
struct Entry {
@@ -92,14 +103,15 @@ private:
};
std::unordered_map<FontStack, Entry, FontStackHash> entries;
- std::mutex mutex;
+
+ void removeGlyphValues(GlyphRequestor& requestor, std::map<uint32_t, GlyphValue>& face);
+ void removePendingRanges(GlyphRequestor& requestor, std::map<GlyphRange, GlyphPBF>& ranges);
- util::WorkQueue workQueue;
GlyphAtlasObserver* observer = nullptr;
BinPack<uint16_t> bin;
AlphaImage image;
- std::atomic<bool> dirty;
+ bool dirty;
mbgl::optional<gl::Texture> texture;
};
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index 5c57d278db..26eff812b7 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -16,6 +16,7 @@ namespace mbgl {
namespace {
+// Parses a Glyph Protobuf and inserts it into the GlyphAtlas. Must be called from main thread.
void parseGlyphPBF(GlyphSet& glyphSet, const GlyphRange& glyphRange, const std::string& data) {
protozero::pbf_reader glyphs_pbf(data);
@@ -117,7 +118,7 @@ GlyphPBF::GlyphPBF(GlyphAtlas* atlas,
observer->onGlyphsLoaded(fontStack, glyphRange);
} else {
try {
- parseGlyphPBF(**atlas->getGlyphSet(fontStack), glyphRange, *res.data);
+ parseGlyphPBF(atlas->getGlyphSet(fontStack), glyphRange, *res.data);
} catch (...) {
observer->onGlyphsError(fontStack, glyphRange, std::current_exception());
return;
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
index 7412ebe411..d5b89cd107 100644
--- a/src/mbgl/text/glyph_pbf.hpp
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -8,10 +8,12 @@
#include <functional>
#include <string>
#include <memory>
+#include <unordered_map>
namespace mbgl {
class GlyphAtlas;
+class GlyphRequestor;
class GlyphAtlasObserver;
class AsyncRequest;
class FileSource;
@@ -29,10 +31,10 @@ public:
return parsed;
}
-private:
- std::atomic<bool> parsed;
+ bool parsed;
std::unique_ptr<AsyncRequest> req;
GlyphAtlasObserver* observer = nullptr;
+ std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors;
};
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp
index ea0dd123db..b8e155502e 100644
--- a/src/mbgl/text/glyph_set.cpp
+++ b/src/mbgl/text/glyph_set.cpp
@@ -1,13 +1,6 @@
-#include <mbgl/math/minmax.hpp>
#include <mbgl/text/glyph_set.hpp>
-#include <mbgl/util/i18n.hpp>
#include <mbgl/util/logging.hpp>
-#include <boost/algorithm/string.hpp>
-
-#include <algorithm>
-#include <cassert>
-
namespace mbgl {
void GlyphSet::insert(uint32_t id, SDFGlyph&& glyph) {
@@ -34,272 +27,4 @@ const std::map<uint32_t, SDFGlyph>& GlyphSet::getSDFs() const {
return sdfs;
}
-const Shaping GlyphSet::getShaping(const std::u16string& logicalInput,
- const float maxWidth,
- const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
- const float spacing,
- const Point<float>& translate,
- const float verticalHeight,
- const WritingModeType writingMode,
- BiDi& bidi) const {
- Shaping shaping(translate.x, translate.y, writingMode);
-
- std::vector<std::u16string> reorderedLines =
- bidi.processText(logicalInput,
- determineLineBreaks(logicalInput, spacing, maxWidth, writingMode));
-
- shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign,
- justify, translate, verticalHeight, writingMode);
-
- return shaping;
-}
-
-void align(Shaping& shaping,
- const float justify,
- const float horizontalAlign,
- const float verticalAlign,
- const float maxLineLength,
- const float lineHeight,
- const std::size_t lineCount,
- const Point<float>& translate) {
- const float shiftX =
- (justify - horizontalAlign) * maxLineLength + ::round(translate.x);
- const float shiftY =
- (-verticalAlign * lineCount + 0.5) * lineHeight + ::round(translate.y);
-
- for (auto& glyph : shaping.positionedGlyphs) {
- glyph.x += shiftX;
- glyph.y += shiftY;
- }
-}
-
-// justify left = 0, right = 1, center = .5
-void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
- const std::map<uint32_t, SDFGlyph>& sdfs,
- std::size_t start,
- std::size_t end,
- float justify) {
- if (!justify) {
- return;
- }
-
- PositionedGlyph& glyph = positionedGlyphs[end];
- auto it = sdfs.find(glyph.glyph);
- if (it != sdfs.end()) {
- const uint32_t lastAdvance = it->second.metrics.advance;
- const float lineIndent = float(glyph.x + lastAdvance) * justify;
-
- for (std::size_t j = start; j <= end; j++) {
- positionedGlyphs[j].x -= lineIndent;
- }
- }
-}
-
-float GlyphSet::determineAverageLineWidth(const std::u16string& logicalInput,
- const float spacing,
- float maxWidth) const {
- float totalWidth = 0;
-
- for (char16_t chr : logicalInput) {
- auto it = sdfs.find(chr);
- if (it != sdfs.end()) {
- totalWidth += it->second.metrics.advance + spacing;
- }
- }
-
- int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth));
- return totalWidth / targetLineCount;
-}
-
-float calculateBadness(const float lineWidth, const float targetWidth, const float penalty, const bool isLastBreak) {
- const float raggedness = std::pow(lineWidth - targetWidth, 2);
- if (isLastBreak) {
- // Favor finals lines shorter than average over longer than average
- if (lineWidth < targetWidth) {
- return raggedness / 2;
- } else {
- return raggedness * 2;
- }
- }
- if (penalty < 0) {
- return raggedness - std::pow(penalty, 2);
- }
- return raggedness + std::pow(penalty, 2);
-}
-
-float calculatePenalty(char16_t codePoint, char16_t nextCodePoint) {
- float penalty = 0;
- // Force break on newline
- if (codePoint == 0x0a) {
- penalty -= 10000;
- }
- // Penalize open parenthesis at end of line
- if (codePoint == 0x28 || codePoint == 0xff08) {
- penalty += 50;
- }
-
- // Penalize close parenthesis at beginning of line
- if (nextCodePoint == 0x29 || nextCodePoint == 0xff09) {
- penalty += 50;
- }
-
- return penalty;
-}
-
-struct PotentialBreak {
- PotentialBreak(const std::size_t p_index, const float p_x, const PotentialBreak* p_priorBreak, const float p_badness)
- : index(p_index), x(p_x), priorBreak(p_priorBreak), badness(p_badness)
- {}
-
- const std::size_t index;
- const float x;
- const PotentialBreak* priorBreak;
- const float badness;
-};
-
-
-PotentialBreak evaluateBreak(const std::size_t breakIndex, const float breakX, const float targetWidth, const std::list<PotentialBreak>& potentialBreaks, const float penalty, const bool isLastBreak) {
- // We could skip evaluating breaks where the line length (breakX - priorBreak.x) > maxWidth
- // ...but in fact we allow lines longer than maxWidth (if there's no break points)
- // ...and when targetWidth and maxWidth are close, strictly enforcing maxWidth can give
- // more lopsided results.
-
- const PotentialBreak* bestPriorBreak = nullptr;
- float bestBreakBadness = calculateBadness(breakX, targetWidth, penalty, isLastBreak);
- for (const auto& potentialBreak : potentialBreaks) {
- const float lineWidth = breakX - potentialBreak.x;
- float breakBadness =
- calculateBadness(lineWidth, targetWidth, penalty, isLastBreak) + potentialBreak.badness;
- if (breakBadness <= bestBreakBadness) {
- bestPriorBreak = &potentialBreak;
- bestBreakBadness = breakBadness;
- }
- }
-
- return PotentialBreak(breakIndex, breakX, bestPriorBreak, bestBreakBadness);
-}
-
-std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) {
- std::set<std::size_t> leastBadBreaks = { lastLineBreak.index };
- const PotentialBreak* priorBreak = lastLineBreak.priorBreak;
- while (priorBreak) {
- leastBadBreaks.insert(priorBreak->index);
- priorBreak = priorBreak->priorBreak;
- }
- return leastBadBreaks;
-}
-
-
-// We determine line breaks based on shaped text in logical order. Working in visual order would be
-// more intuitive, but we can't do that because the visual order may be changed by line breaks!
-std::set<std::size_t> GlyphSet::determineLineBreaks(const std::u16string& logicalInput,
- const float spacing,
- float maxWidth,
- const WritingModeType writingMode) const {
- if (!maxWidth || writingMode != WritingModeType::Horizontal) {
- return {};
- }
-
- if (logicalInput.empty()) {
- return {};
- }
-
- const float targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth);
-
- std::list<PotentialBreak> potentialBreaks;
- float currentX = 0;
-
- for (std::size_t i = 0; i < logicalInput.size(); i++) {
- const char16_t codePoint = logicalInput[i];
- auto it = sdfs.find(codePoint);
- if (it != sdfs.end() && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) {
- currentX += it->second.metrics.advance + spacing;
- }
-
- // Ideographic characters, spaces, and word-breaking punctuation that often appear without
- // surrounding spaces.
- if ((i < logicalInput.size() - 1) &&
- (util::i18n::allowsWordBreaking(codePoint) || util::i18n::allowsIdeographicBreaking(codePoint))) {
- potentialBreaks.push_back(evaluateBreak(i+1, currentX, targetWidth, potentialBreaks,
- calculatePenalty(codePoint, logicalInput[i+1]),
- false));
- }
- }
-
- return leastBadBreaks(evaluateBreak(logicalInput.size(), currentX, targetWidth, potentialBreaks, 0, true));
-}
-
-void GlyphSet::shapeLines(Shaping& shaping,
- const std::vector<std::u16string>& lines,
- const float spacing,
- const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
- const Point<float>& translate,
- const float verticalHeight,
- const WritingModeType writingMode) const {
-
- // the y offset *should* be part of the font metadata
- const int32_t yOffset = -17;
-
- float x = 0;
- float y = yOffset;
-
- float maxLineLength = 0;
-
- for (std::u16string line : lines) {
- // Collapse whitespace so it doesn't throw off justification
- boost::algorithm::trim_if(line, boost::algorithm::is_any_of(u" \t\n\v\f\r"));
-
- if (line.empty()) {
- y += lineHeight; // Still need a line feed after empty line
- continue;
- }
-
- std::size_t lineStartIndex = shaping.positionedGlyphs.size();
- for (char16_t chr : line) {
- auto it = sdfs.find(chr);
- if (it == sdfs.end()) {
- continue;
- }
-
- const SDFGlyph& glyph = it->second;
-
- if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
- shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
- x += glyph.metrics.advance + spacing;
- } else {
- shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2);
- x += verticalHeight + spacing;
- }
- }
-
- // Only justify if we placed at least one glyph
- if (shaping.positionedGlyphs.size() != lineStartIndex) {
- float lineLength = x - spacing; // Don't count trailing spacing
- maxLineLength = util::max(lineLength, maxLineLength);
-
- justifyLine(shaping.positionedGlyphs, sdfs, lineStartIndex,
- shaping.positionedGlyphs.size() - 1, justify);
- }
-
- x = 0;
- y += lineHeight;
- }
-
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
- lines.size(), translate);
- const uint32_t height = lines.size() * lineHeight;
-
- // Calculate the bounding box
- shaping.top += -verticalAlign * height;
- shaping.bottom = shaping.top + height;
- shaping.left += -horizontalAlign * maxLineLength;
- shaping.right = shaping.left + maxLineLength;
-}
-
} // end namespace mbgl
diff --git a/src/mbgl/text/glyph_set.hpp b/src/mbgl/text/glyph_set.hpp
index 0342c82eb5..9f4bef94d2 100644
--- a/src/mbgl/text/glyph_set.hpp
+++ b/src/mbgl/text/glyph_set.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/text/bidi.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/geometry.hpp>
@@ -10,38 +9,8 @@ class GlyphSet {
public:
void insert(uint32_t id, SDFGlyph&&);
const std::map<uint32_t, SDFGlyph>& getSDFs() const;
- const Shaping getShaping(const std::u16string& string,
- float maxWidth,
- float lineHeight,
- float horizontalAlign,
- float verticalAlign,
- float justify,
- float spacing,
- const Point<float>& translate,
- float verticalHeight,
- const WritingModeType,
- BiDi& bidi) const;
private:
- float determineAverageLineWidth(const std::u16string& logicalInput,
- const float spacing,
- float maxWidth) const;
- std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
- const float spacing,
- float maxWidth,
- const WritingModeType) const;
-
- void shapeLines(Shaping& shaping,
- const std::vector<std::u16string>& lines,
- const float spacing,
- float lineHeight,
- float horizontalAlign,
- float verticalAlign,
- float justify,
- const Point<float>& translate,
- float verticalHeight,
- const WritingModeType) const;
-
std::map<uint32_t, SDFGlyph> sdfs;
};
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index e68566d419..aa760ea4fe 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -1,17 +1,297 @@
#include <mbgl/text/shaping.hpp>
+#include <mbgl/util/i18n.hpp>
#include <mbgl/layout/symbol_feature.hpp>
+#include <mbgl/math/minmax.hpp>
+#include <mbgl/text/bidi.hpp>
+
+#include <boost/algorithm/string.hpp>
+
+#include <algorithm>
namespace mbgl {
PositionedIcon shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.spriteImage->getWidth() / 2.0f;
- float x2 = x1 + image.spriteImage->getWidth();
- float y1 = dy - image.spriteImage->getHeight() / 2.0f;
- float y2 = y1 + image.spriteImage->getHeight();
+ float x1 = dx - image.width/ 2.0f;
+ float x2 = x1 + image.width;
+ float y1 = dy - image.height / 2.0f;
+ float y2 = y1 + image.height;
return PositionedIcon(image, y1, y2, x1, x2, iconRotation);
}
+void align(Shaping& shaping,
+ const float justify,
+ const float horizontalAlign,
+ const float verticalAlign,
+ const float maxLineLength,
+ const float lineHeight,
+ const std::size_t lineCount,
+ const Point<float>& translate) {
+ const float shiftX =
+ (justify - horizontalAlign) * maxLineLength + ::round(translate.x);
+ const float shiftY =
+ (-verticalAlign * lineCount + 0.5) * lineHeight + ::round(translate.y);
+
+ for (auto& glyph : shaping.positionedGlyphs) {
+ glyph.x += shiftX;
+ glyph.y += shiftY;
+ }
+}
+
+// justify left = 0, right = 1, center = .5
+void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
+ const GlyphPositions& glyphs,
+ std::size_t start,
+ std::size_t end,
+ float justify) {
+ if (!justify) {
+ return;
+ }
+
+ PositionedGlyph& glyph = positionedGlyphs[end];
+ auto it = glyphs.find(glyph.glyph);
+ if (it != glyphs.end()) {
+ const uint32_t lastAdvance = it->second.metrics.advance;
+ const float lineIndent = float(glyph.x + lastAdvance) * justify;
+
+ for (std::size_t j = start; j <= end; j++) {
+ positionedGlyphs[j].x -= lineIndent;
+ }
+ }
+}
+
+float determineAverageLineWidth(const std::u16string& logicalInput,
+ const float spacing,
+ float maxWidth,
+ const GlyphPositions& glyphs) {
+ float totalWidth = 0;
+
+ for (char16_t chr : logicalInput) {
+ auto it = glyphs.find(chr);
+ if (it != glyphs.end()) {
+ totalWidth += it->second.metrics.advance + spacing;
+ }
+ }
+
+ int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth));
+ return totalWidth / targetLineCount;
+}
+
+float calculateBadness(const float lineWidth, const float targetWidth, const float penalty, const bool isLastBreak) {
+ const float raggedness = std::pow(lineWidth - targetWidth, 2);
+ if (isLastBreak) {
+ // Favor finals lines shorter than average over longer than average
+ if (lineWidth < targetWidth) {
+ return raggedness / 2;
+ } else {
+ return raggedness * 2;
+ }
+ }
+ if (penalty < 0) {
+ return raggedness - std::pow(penalty, 2);
+ }
+ return raggedness + std::pow(penalty, 2);
+}
+
+float calculatePenalty(char16_t codePoint, char16_t nextCodePoint) {
+ float penalty = 0;
+ // Force break on newline
+ if (codePoint == 0x0a) {
+ penalty -= 10000;
+ }
+ // Penalize open parenthesis at end of line
+ if (codePoint == 0x28 || codePoint == 0xff08) {
+ penalty += 50;
+ }
+
+ // Penalize close parenthesis at beginning of line
+ if (nextCodePoint == 0x29 || nextCodePoint == 0xff09) {
+ penalty += 50;
+ }
+
+ return penalty;
+}
+
+struct PotentialBreak {
+ PotentialBreak(const std::size_t p_index, const float p_x, const PotentialBreak* p_priorBreak, const float p_badness)
+ : index(p_index), x(p_x), priorBreak(p_priorBreak), badness(p_badness)
+ {}
+
+ const std::size_t index;
+ const float x;
+ const PotentialBreak* priorBreak;
+ const float badness;
+};
+
+
+PotentialBreak evaluateBreak(const std::size_t breakIndex, const float breakX, const float targetWidth, const std::list<PotentialBreak>& potentialBreaks, const float penalty, const bool isLastBreak) {
+ // We could skip evaluating breaks where the line length (breakX - priorBreak.x) > maxWidth
+ // ...but in fact we allow lines longer than maxWidth (if there's no break points)
+ // ...and when targetWidth and maxWidth are close, strictly enforcing maxWidth can give
+ // more lopsided results.
+
+ const PotentialBreak* bestPriorBreak = nullptr;
+ float bestBreakBadness = calculateBadness(breakX, targetWidth, penalty, isLastBreak);
+ for (const auto& potentialBreak : potentialBreaks) {
+ const float lineWidth = breakX - potentialBreak.x;
+ float breakBadness =
+ calculateBadness(lineWidth, targetWidth, penalty, isLastBreak) + potentialBreak.badness;
+ if (breakBadness <= bestBreakBadness) {
+ bestPriorBreak = &potentialBreak;
+ bestBreakBadness = breakBadness;
+ }
+ }
+
+ return PotentialBreak(breakIndex, breakX, bestPriorBreak, bestBreakBadness);
+}
+
+std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) {
+ std::set<std::size_t> leastBadBreaks = { lastLineBreak.index };
+ const PotentialBreak* priorBreak = lastLineBreak.priorBreak;
+ while (priorBreak) {
+ leastBadBreaks.insert(priorBreak->index);
+ priorBreak = priorBreak->priorBreak;
+ }
+ return leastBadBreaks;
+}
+
+
+// We determine line breaks based on shaped text in logical order. Working in visual order would be
+// more intuitive, but we can't do that because the visual order may be changed by line breaks!
+std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
+ const float spacing,
+ float maxWidth,
+ const WritingModeType writingMode,
+ const GlyphPositions& glyphs) {
+ if (!maxWidth || writingMode != WritingModeType::Horizontal) {
+ return {};
+ }
+
+ if (logicalInput.empty()) {
+ return {};
+ }
+
+ const float targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphs);
+
+ std::list<PotentialBreak> potentialBreaks;
+ float currentX = 0;
+
+ for (std::size_t i = 0; i < logicalInput.size(); i++) {
+ const char16_t codePoint = logicalInput[i];
+ auto it = glyphs.find(codePoint);
+ if (it != glyphs.end() && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) {
+ currentX += it->second.metrics.advance + spacing;
+ }
+
+ // Ideographic characters, spaces, and word-breaking punctuation that often appear without
+ // surrounding spaces.
+ if ((i < logicalInput.size() - 1) &&
+ (util::i18n::allowsWordBreaking(codePoint) || util::i18n::allowsIdeographicBreaking(codePoint))) {
+ potentialBreaks.push_back(evaluateBreak(i+1, currentX, targetWidth, potentialBreaks,
+ calculatePenalty(codePoint, logicalInput[i+1]),
+ false));
+ }
+ }
+
+ return leastBadBreaks(evaluateBreak(logicalInput.size(), currentX, targetWidth, potentialBreaks, 0, true));
+}
+
+void shapeLines(Shaping& shaping,
+ const std::vector<std::u16string>& lines,
+ const float spacing,
+ const float lineHeight,
+ const float horizontalAlign,
+ const float verticalAlign,
+ const float justify,
+ const Point<float>& translate,
+ const float verticalHeight,
+ const WritingModeType writingMode,
+ const GlyphPositions& glyphs) {
+
+ // the y offset *should* be part of the font metadata
+ const int32_t yOffset = -17;
+
+ float x = 0;
+ float y = yOffset;
+
+ float maxLineLength = 0;
+
+ for (std::u16string line : lines) {
+ // Collapse whitespace so it doesn't throw off justification
+ boost::algorithm::trim_if(line, boost::algorithm::is_any_of(u" \t\n\v\f\r"));
+
+ if (line.empty()) {
+ y += lineHeight; // Still need a line feed after empty line
+ continue;
+ }
+
+ std::size_t lineStartIndex = shaping.positionedGlyphs.size();
+ for (char16_t chr : line) {
+ auto it = glyphs.find(chr);
+ if (it == glyphs.end()) {
+ continue;
+ }
+
+ const Glyph& glyph = it->second;
+
+ if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
+ shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
+ x += glyph.metrics.advance + spacing;
+ } else {
+ shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2);
+ x += verticalHeight + spacing;
+ }
+ }
+
+ // Only justify if we placed at least one glyph
+ if (shaping.positionedGlyphs.size() != lineStartIndex) {
+ float lineLength = x - spacing; // Don't count trailing spacing
+ maxLineLength = util::max(lineLength, maxLineLength);
+
+ justifyLine(shaping.positionedGlyphs, glyphs, lineStartIndex,
+ shaping.positionedGlyphs.size() - 1, justify);
+ }
+
+ x = 0;
+ y += lineHeight;
+ }
+
+ align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
+ lines.size(), translate);
+ const uint32_t height = lines.size() * lineHeight;
+
+ // Calculate the bounding box
+ shaping.top += -verticalAlign * height;
+ shaping.bottom = shaping.top + height;
+ shaping.left += -horizontalAlign * maxLineLength;
+ shaping.right = shaping.left + maxLineLength;
+}
+
+const Shaping getShaping(const std::u16string& logicalInput,
+ const float maxWidth,
+ const float lineHeight,
+ const float horizontalAlign,
+ const float verticalAlign,
+ const float justify,
+ const float spacing,
+ const Point<float>& translate,
+ const float verticalHeight,
+ const WritingModeType writingMode,
+ BiDi& bidi,
+ const GlyphPositions& glyphs) {
+ Shaping shaping(translate.x, translate.y, writingMode);
+
+ std::vector<std::u16string> reorderedLines =
+ bidi.processText(logicalInput,
+ determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs));
+
+ shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign,
+ justify, translate, verticalHeight, writingMode, glyphs);
+
+ return shaping;
+}
+
+
} // namespace mbgl
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 1b7b8b2733..fc404e3a97 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -9,6 +9,7 @@ namespace mbgl {
class SpriteAtlasElement;
class SymbolFeature;
+class BiDi;
class PositionedIcon {
public:
@@ -37,5 +38,18 @@ public:
};
PositionedIcon shapeIcon(const SpriteAtlasElement&, const std::array<float, 2>& iconOffset, const float iconRotation);
+
+const Shaping getShaping(const std::u16string& string,
+ float maxWidth,
+ float lineHeight,
+ float horizontalAlign,
+ float verticalAlign,
+ float justify,
+ float spacing,
+ const Point<float>& translate,
+ float verticalHeight,
+ const WritingModeType,
+ BiDi& bidi,
+ const GlyphPositions& glyphs);
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 5ccc037ce0..4e67144082 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -18,6 +18,8 @@
#include <mbgl/style/query.hpp>
#include <mbgl/util/logging.hpp>
+#include <iostream>
+
namespace mbgl {
using namespace style;
@@ -32,12 +34,16 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_,
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
- *parameters.style.glyphAtlas,
obsolete,
- parameters.mode) {
+ parameters.mode),
+ glyphAtlas(*parameters.style.glyphAtlas) {
}
GeometryTile::~GeometryTile() {
+ glyphAtlas.removeGlyphs(*this);
+ for (auto spriteAtlas : pendingSpriteAtlases) {
+ spriteAtlas->removeRequestor(*this);
+ }
cancel();
}
@@ -77,10 +83,6 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
worker.invoke(&GeometryTileWorker::setPlacementConfig, desiredConfig, correlationID);
}
-void GeometryTile::symbolDependenciesChanged() {
- worker.invoke(&GeometryTileWorker::symbolDependenciesChanged);
-}
-
void GeometryTile::redoLayout() {
// Mark the tile as pending again if it was complete before to prevent signaling a complete
// state despite pending parse operations.
@@ -130,6 +132,31 @@ void GeometryTile::onError(std::exception_ptr err) {
availableData = DataAvailability::All;
observer->onTileError(*this, err);
}
+
+void GeometryTile::onGlyphsAvailable(GlyphPositionMap glyphPositions, GlyphRangeSet loadedRanges) {
+ worker.invoke(&GeometryTileWorker::onGlyphsAvailable, std::move(glyphPositions), std::move(loadedRanges));
+}
+
+void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
+ glyphAtlas.getGlyphs(*this, std::move(glyphDependencies));
+}
+
+void GeometryTile::onIconsAvailable(SpriteAtlas* spriteAtlas, IconMap icons) {
+ iconAtlasMap[(uintptr_t)spriteAtlas] = icons;
+ pendingSpriteAtlases.erase(spriteAtlas);
+ if (pendingSpriteAtlases.empty()) {
+ worker.invoke(&GeometryTileWorker::onIconsAvailable, std::move(iconAtlasMap));
+ }
+}
+
+// TODO: If there's any value to be gained by it, we can narrow our request to just the sprites
+// we need, but SpriteAtlases are just "loaded" or "not loaded"
+void GeometryTile::getIcons(IconDependencyMap iconDependencyMap) {
+ for (auto dependency : iconDependencyMap) {
+ pendingSpriteAtlases.insert(dependency.first);
+ dependency.first->getIcons(*this);
+ }
+}
Bucket* GeometryTile::getBucket(const Layer& layer) {
const auto& buckets = layer.is<SymbolLayer>() ? symbolBuckets : nonSymbolBuckets;
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index cabe193467..f057e21507 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -1,7 +1,9 @@
#pragma once
+#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/geometry_tile_worker.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/placement_config.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/actor/actor.hpp>
@@ -24,7 +26,7 @@ class UpdateParameters;
class SourceQueryOptions;
} // namespace style
-class GeometryTile : public Tile {
+class GeometryTile : public Tile, public GlyphRequestor, IconRequestor {
public:
GeometryTile(const OverscaledTileID&,
std::string sourceID,
@@ -36,8 +38,13 @@ public:
void setData(std::unique_ptr<const GeometryTileData>);
void setPlacementConfig(const PlacementConfig&) override;
- void symbolDependenciesChanged() override;
void redoLayout() override;
+
+ void onGlyphsAvailable(GlyphPositionMap, GlyphRangeSet) override;
+ void onIconsAvailable(SpriteAtlas*, IconMap) override;
+
+ void getGlyphs(GlyphDependencies);
+ void getIcons(IconDependencyMap);
Bucket* getBucket(const style::Layer&) override;
@@ -87,6 +94,10 @@ private:
std::shared_ptr<Mailbox> mailbox;
Actor<GeometryTileWorker> worker;
+ GlyphAtlas& glyphAtlas;
+ std::set<SpriteAtlas*> pendingSpriteAtlases;
+ IconAtlasMap iconAtlasMap;
+
uint64_t correlationID = 0;
optional<PlacementConfig> requestedConfig;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index b1fd7a852e..d1d4c9e9b8 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -2,8 +2,8 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/geometry_tile.hpp>
#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/style/bucket_parameters.hpp>
#include <mbgl/style/group_by_layout.hpp>
#include <mbgl/style/filter.hpp>
@@ -25,19 +25,16 @@ using namespace style;
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
- GlyphAtlas& glyphAtlas_,
const std::atomic<bool>& obsolete_,
const MapMode mode_)
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
- glyphAtlas(glyphAtlas_),
obsolete(obsolete_),
mode(mode_) {
}
GeometryTileWorker::~GeometryTileWorker() {
- glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
}
/*
@@ -147,14 +144,14 @@ void GeometryTileWorker::symbolDependenciesChanged() {
try {
switch (state) {
case Idle:
- if (hasPendingSymbolDependencies()) {
+ if (hasPendingSymbolLayouts()) {
attemptPlacement();
coalesce();
}
break;
case Coalescing:
- if (hasPendingSymbolDependencies()) {
+ if (hasPendingSymbolLayouts()) {
state = NeedPlacement;
}
break;
@@ -199,6 +196,71 @@ void GeometryTileWorker::coalesce() {
self.invoke(&GeometryTileWorker::coalesced);
}
+
+void GeometryTileWorker::onGlyphsAvailable(GlyphPositionMap newGlyphPositions, GlyphRangeSet loadedRanges) {
+ GlyphDependencies loadedGlyphs;
+ for (auto& pendingFontGlyphs : pendingGlyphDependencies) {
+ auto newFontGlyphs = newGlyphPositions.find(pendingFontGlyphs.first);
+ for (auto glyphID : pendingFontGlyphs.second) {
+ if (newFontGlyphs != newGlyphPositions.end()) {
+ auto newFontGlyph = newFontGlyphs->second.find(glyphID);
+ if (newFontGlyph != newFontGlyphs->second.end()) {
+ glyphPositions[pendingFontGlyphs.first].emplace(glyphID, newFontGlyph->second);
+ }
+ }
+ if (loadedRanges.find(getGlyphRange(glyphID)) != loadedRanges.end()) {
+ // Erase the glyph from our pending font set as long as its range is loaded
+ // If the glyph itself is missing, that means we can't get a glyph for
+ // this fontstack, and we go ahead and render with missing glyphs
+ loadedGlyphs[pendingFontGlyphs.first].insert(glyphID);
+ }
+ }
+ }
+
+ for (auto& loadedFont : loadedGlyphs) {
+ for (auto loadedGlyph : loadedFont.second) {
+ pendingGlyphDependencies[loadedFont.first].erase(loadedGlyph);
+ }
+ }
+ symbolDependenciesChanged();
+}
+
+void GeometryTileWorker::onIconsAvailable(IconAtlasMap newIcons) {
+ for (auto& atlasIcons : newIcons) {
+ auto pendingAtlasIcons = pendingIconDependencies.find((SpriteAtlas*)atlasIcons.first);
+ if (pendingAtlasIcons != pendingIconDependencies.end()) {
+ icons[atlasIcons.first] = std::move(newIcons[atlasIcons.first]);
+ pendingIconDependencies.erase((SpriteAtlas*)atlasIcons.first);
+ }
+ }
+ symbolDependenciesChanged();
+}
+
+void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependencies) {
+ for (auto& fontDependencies : glyphDependencies) {
+ auto fontGlyphs = glyphPositions.find(fontDependencies.first);
+ for (auto glyphID : fontDependencies.second) {
+ if (fontGlyphs == glyphPositions.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) {
+ pendingGlyphDependencies[fontDependencies.first].insert(glyphID);
+ }
+ }
+ }
+ if (!pendingGlyphDependencies.empty()) {
+ parent.invoke(&GeometryTile::getGlyphs, pendingGlyphDependencies);
+ }
+}
+
+void GeometryTileWorker::requestNewIcons(const IconDependencyMap &iconDependencies) {
+ for (auto& atlasDependency : iconDependencies) {
+ if (icons.find((uintptr_t)atlasDependency.first) == icons.end()) {
+ pendingIconDependencies[atlasDependency.first] = IconDependencies();
+ }
+ }
+ if (!pendingIconDependencies.empty()) {
+ parent.invoke(&GeometryTile::getIcons, pendingIconDependencies);
+ }
+}
+
void GeometryTileWorker::redoLayout() {
if (!data || !layers) {
return;
@@ -215,6 +277,9 @@ void GeometryTileWorker::redoLayout() {
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
auto featureIndex = std::make_unique<FeatureIndex>();
BucketParameters parameters { id, mode };
+
+ GlyphDependencies glyphDependencies;
+ IconDependencyMap iconDependencyMap;
std::vector<std::vector<const Layer*>> groups = groupByLayout(*layers);
for (auto& group : groups) {
@@ -242,7 +307,7 @@ void GeometryTileWorker::redoLayout() {
if (leader.is<SymbolLayer>()) {
symbolLayoutMap.emplace(leader.getID(),
- leader.as<SymbolLayer>()->impl->createLayout(parameters, group, *geometryLayer));
+ leader.as<SymbolLayer>()->impl->createLayout(parameters, group, *geometryLayer, glyphDependencies, iconDependencyMap));
} else {
const Filter& filter = leader.baseImpl->filter;
const std::string& sourceLayerID = leader.baseImpl->sourceLayer;
@@ -276,6 +341,9 @@ void GeometryTileWorker::redoLayout() {
symbolLayouts.push_back(std::move(it->second));
}
}
+
+ requestNewGlyphs(glyphDependencies);
+ requestNewIcons(iconDependencyMap);
parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
std::move(buckets),
@@ -287,46 +355,31 @@ void GeometryTileWorker::redoLayout() {
attemptPlacement();
}
-bool GeometryTileWorker::hasPendingSymbolDependencies() const {
- bool result = false;
-
+bool GeometryTileWorker::hasPendingSymbolLayouts() const {
for (const auto& symbolLayout : symbolLayouts) {
if (symbolLayout->state == SymbolLayout::Pending) {
- result = true;
+ return true;
}
}
- return result;
+ return false;
}
-void GeometryTileWorker::attemptPlacement() {
- if (!data || !layers || !placementConfig) {
- return;
- }
-
- bool canPlace = true;
-
- // Prepare as many SymbolLayouts as possible.
- for (auto& symbolLayout : symbolLayouts) {
- if (obsolete) {
- return;
- }
-
- if (symbolLayout->state == SymbolLayout::Pending) {
- if (symbolLayout->canPrepare(glyphAtlas)) {
- symbolLayout->state = SymbolLayout::Prepared;
- symbolLayout->prepare(reinterpret_cast<uintptr_t>(this),
- glyphAtlas);
- } else {
- canPlace = false;
- }
+bool GeometryTileWorker::hasPendingSymbolDependencies() const {
+ for (auto& glyphDependency : pendingGlyphDependencies) {
+ if (!glyphDependency.second.empty()) {
+ return true;
}
}
+ return !pendingIconDependencies.empty();
+}
- if (!canPlace) {
- return; // We'll be notified (via `setPlacementConfig`) when it's time to try again.
- }
+void GeometryTileWorker::attemptPlacement() {
+ if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
+ return;
+ }
+
auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
@@ -334,8 +387,12 @@ void GeometryTileWorker::attemptPlacement() {
if (obsolete) {
return;
}
-
- symbolLayout->state = SymbolLayout::Placed;
+
+ if (symbolLayout->state == SymbolLayout::Pending) {
+ symbolLayout->prepare(glyphPositions,icons);
+ symbolLayout->state = SymbolLayout::Placed;
+ }
+
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 91bf81a697..39f4411e23 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -2,6 +2,8 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/text/glyph.hpp>
#include <mbgl/text/placement_config.hpp>
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/util/optional.hpp>
@@ -25,7 +27,6 @@ public:
GeometryTileWorker(ActorRef<GeometryTileWorker> self,
ActorRef<GeometryTile> parent,
OverscaledTileID,
- GlyphAtlas&,
const std::atomic<bool>&,
const MapMode);
~GeometryTileWorker();
@@ -33,20 +34,28 @@ public:
void setLayers(std::vector<std::unique_ptr<style::Layer>>, uint64_t correlationID);
void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
void setPlacementConfig(PlacementConfig, uint64_t correlationID);
- void symbolDependenciesChanged();
+
+ void onGlyphsAvailable(GlyphPositionMap glyphs, GlyphRangeSet loadedRanges);
+ void onIconsAvailable(IconAtlasMap icons);
private:
- void coalesce();
void coalesced();
void redoLayout();
void attemptPlacement();
+
+ void coalesce();
+
+ void requestNewGlyphs(const GlyphDependencies&);
+ void requestNewIcons(const IconDependencyMap&);
+
+ void symbolDependenciesChanged();
bool hasPendingSymbolDependencies() const;
+ bool hasPendingSymbolLayouts() const;
ActorRef<GeometryTileWorker> self;
ActorRef<GeometryTile> parent;
const OverscaledTileID id;
- GlyphAtlas& glyphAtlas;
const std::atomic<bool>& obsolete;
const MapMode mode;
@@ -66,6 +75,10 @@ private:
optional<PlacementConfig> placementConfig;
std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
+ GlyphDependencies pendingGlyphDependencies;
+ IconDependencyMap pendingIconDependencies;
+ GlyphPositionMap glyphPositions;
+ IconAtlasMap icons;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 613b15f36c..a5f574b567 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -50,7 +50,6 @@ public:
virtual Bucket* getBucket(const style::Layer&) = 0;
virtual void setPlacementConfig(const PlacementConfig&) {}
- virtual void symbolDependenciesChanged() {};
virtual void redoLayout() {}
virtual void queryRenderedFeatures(
diff --git a/src/mbgl/util/exclusive.hpp b/src/mbgl/util/exclusive.hpp
deleted file mode 100644
index 844588dc90..0000000000
--- a/src/mbgl/util/exclusive.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <mutex>
-
-
-namespace mbgl {
-namespace util {
-
-template <class T>
-class exclusive {
-public:
- exclusive(T* val, std::unique_ptr<std::lock_guard<std::mutex>> mtx) : ptr(val), lock(std::move(mtx)) {}
-
- T* operator->() { return ptr; }
- const T* operator->() const { return ptr; }
- T* operator*() { return ptr; }
- const T* operator*() const { return ptr; }
-
-private:
- T *ptr;
- std::unique_ptr<std::lock_guard<std::mutex>> lock;
-};
-
-
-} // end namespace util
-} // end namespace mbgl