summaryrefslogtreecommitdiff
path: root/src/mbgl/text/cross_tile_symbol_index.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text/cross_tile_symbol_index.cpp')
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp190
1 files changed, 190 insertions, 0 deletions
diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp
new file mode 100644
index 0000000000..fadc33acc8
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -0,0 +1,190 @@
+#include <mbgl/text/cross_tile_symbol_index.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
+
+namespace mbgl {
+
+
+TileLayerIndex::TileLayerIndex(OverscaledTileID coord, std::shared_ptr<std::vector<SymbolInstance>> symbolInstances)
+ : coord(coord), symbolInstances(symbolInstances) {
+ for (SymbolInstance& symbolInstance : *symbolInstances) {
+ if (indexedSymbolInstances.find(symbolInstance.key) == indexedSymbolInstances.end()) {
+ indexedSymbolInstances.emplace(symbolInstance.key, std::vector<IndexedSymbolInstance>{});
+ }
+
+ indexedSymbolInstances.at(symbolInstance.key).emplace_back(symbolInstance, getScaledCoordinates(symbolInstance, coord));
+
+ symbolInstance.isDuplicate = false;
+ // symbolInstance.isDuplicate = false;
+ // If we don't pick up an opacity from our parent or child tiles
+ // Reset so that symbols in cached tiles fade in the same
+ // way as freshly loaded tiles
+ symbolInstance.textOpacityState = OpacityState{};
+ symbolInstance.iconOpacityState = OpacityState{};
+ }
+ }
+
+Point<double> TileLayerIndex::getScaledCoordinates(SymbolInstance& symbolInstance, OverscaledTileID& childTileCoord) {
+ // Round anchor positions to roughly 4 pixel grid
+ const double roundingFactor = 512.0 / util::EXTENT / 2.0;
+ const double scale = roundingFactor / std::pow(2, childTileCoord.canonical.z - coord.canonical.z);
+ return {
+ std::floor((childTileCoord.canonical.x * util::EXTENT + symbolInstance.anchor.point.x) * scale),
+ std::floor((childTileCoord.canonical.y * util::EXTENT + symbolInstance.anchor.point.y) * scale)
+ };
+}
+
+optional<SymbolInstance> TileLayerIndex::getMatchingSymbol(SymbolInstance& childTileSymbol, OverscaledTileID& childTileCoord) {
+ auto it = indexedSymbolInstances.find(childTileSymbol.key);
+ if (it == indexedSymbolInstances.end()) return {};
+
+ Point<double> childTileSymbolCoord = getScaledCoordinates(childTileSymbol, childTileCoord);
+
+ for (IndexedSymbolInstance& thisTileSymbol: it->second) {
+ // Return any symbol with the same keys whose coordinates are within 1
+ // grid unit. (with a 4px grid, this covers a 12px by 12px area)
+ if (std::fabs(thisTileSymbol.coord.x - childTileSymbolCoord.x) <= 1 &&
+ std::fabs(thisTileSymbol.coord.y - childTileSymbolCoord.y) <= 1) {
+ return { thisTileSymbol.instance };
+ }
+ }
+
+ return {};
+}
+
+CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() {
+}
+
+void CrossTileSymbolLayerIndex::addTile(const OverscaledTileID& coord, std::shared_ptr<std::vector<SymbolInstance>> symbolInstances) {
+ uint8_t minZoom = 25;
+ uint8_t maxZoom = 0;
+ for (auto& it : indexes) {
+ auto z = it.first;
+ minZoom = std::min(minZoom, z);
+ maxZoom = std::max(maxZoom, z);
+ }
+
+ TileLayerIndex tileIndex(coord, symbolInstances);
+
+ // make all higher-res child tiles block duplicate labels in this tile
+ for (auto z = maxZoom; z > coord.overscaledZ; z--) {
+ auto zoomIndexes = indexes.find(coord.overscaledZ);
+ if (zoomIndexes != indexes.end()) {
+ for (auto& childIndex : zoomIndexes->second) {
+ if (!childIndex.second.coord.isChildOf(coord)) continue;
+ // Mark labels in this tile blocked, and don't copy opacity state
+ // into this tile
+ blockLabels(childIndex.second, tileIndex, false);
+ }
+ }
+ }
+
+ // make this tile block duplicate labels in lower-res parent tiles
+ auto parentCoord = std::make_unique<OverscaledTileID>(coord);
+ for (auto z = coord.overscaledZ - 1; z >= minZoom; z--) {
+ parentCoord = std::make_unique<OverscaledTileID>(*(parentCoord->parent()));
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ auto parentIndex = zoomIndexes->second.find(*parentCoord);
+ if (parentIndex != zoomIndexes->second.end()) {
+ // Mark labels in the parent tile blocked, and copy opacity state
+ // into this tile
+ blockLabels(tileIndex, parentIndex->second, true);
+ }
+ }
+ }
+
+ if (indexes.find(coord.overscaledZ) == indexes.end()) {
+ indexes.emplace(coord.overscaledZ, std::map<OverscaledTileID,TileLayerIndex>{});
+ }
+
+ indexes.at(coord.overscaledZ).emplace(coord, std::move(tileIndex));
+}
+
+void CrossTileSymbolLayerIndex::removeTile(const OverscaledTileID& coord) {
+
+ auto removedIndex = indexes.at(coord.overscaledZ).at(coord);
+
+ uint8_t minZoom = 25;
+ for (auto& it : indexes) {
+ auto z = it.first;
+ minZoom = std::min(minZoom, z);
+ }
+
+ auto parentCoord = std::make_unique<OverscaledTileID>(coord);
+ for (auto z = coord.overscaledZ - 1; z >= minZoom; z--) {
+ parentCoord = std::make_unique<OverscaledTileID>(*(parentCoord->parent()));
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ auto parentIndex = zoomIndexes->second.find(*parentCoord);
+ if (parentIndex != zoomIndexes->second.end()) {
+ unblockLabels(removedIndex, parentIndex->second);
+ }
+ }
+ }
+
+ indexes.at(coord.overscaledZ).erase(coord);
+ if (indexes.at(coord.overscaledZ).size() == 0) {
+ indexes.erase(coord.overscaledZ);
+ }
+}
+
+void CrossTileSymbolLayerIndex::blockLabels(TileLayerIndex& childIndex, TileLayerIndex& parentIndex, bool copyParentOpacity) {
+ for (auto& symbolInstance : *childIndex.symbolInstances) {
+ // only non-duplicate labels can block other labels
+ if (!symbolInstance.isDuplicate) {
+ auto parentSymbolInstance = parentIndex.getMatchingSymbol(symbolInstance, childIndex.coord);
+ if (parentSymbolInstance) {
+ // if the parent label was previously non-duplicate, make it duplicate because it's now blocked
+ if (!parentSymbolInstance->isDuplicate) {
+ parentSymbolInstance->isDuplicate = true;
+
+ // If the child label is the one being added to the index,
+ // copy the parent's opacity to the child
+ if (copyParentOpacity) {
+ symbolInstance.textOpacityState = parentSymbolInstance->textOpacityState;
+ symbolInstance.iconOpacityState = parentSymbolInstance->iconOpacityState;
+ }
+ }
+ }
+ }
+ }
+}
+
+void CrossTileSymbolLayerIndex::unblockLabels(TileLayerIndex& childIndex, TileLayerIndex& parentIndex) {
+ assert(childIndex.coord.overscaledZ > parentIndex.coord.overscaledZ);
+ for (auto& symbolInstance : *childIndex.symbolInstances) {
+ // only non-duplicate labels were blocking other labels
+ if (!symbolInstance.isDuplicate) {
+ auto parentSymbolInstance = parentIndex.getMatchingSymbol(symbolInstance, childIndex.coord);
+ if (parentSymbolInstance) {
+ // this label is now unblocked, copy its opacity state
+ parentSymbolInstance->isDuplicate = false;
+ parentSymbolInstance->textOpacityState = symbolInstance.textOpacityState;
+ parentSymbolInstance->iconOpacityState = symbolInstance.iconOpacityState;
+
+ // mark child as duplicate so that it doesn't unblock further tiles at lower res
+ // in the remaining calls to unblockLabels before it's fully removed
+ symbolInstance.isDuplicate = true;
+ }
+ }
+ }
+}
+
+CrossTileSymbolIndex::CrossTileSymbolIndex() {}
+
+void CrossTileSymbolIndex::addTileLayer(std::string& layerId, const OverscaledTileID& coord, std::shared_ptr<std::vector<SymbolInstance>> symbolInstances) {
+ if (layerIndexes.find(layerId) == layerIndexes.end()) {
+ layerIndexes.emplace(layerId, CrossTileSymbolLayerIndex{});
+ }
+
+ CrossTileSymbolLayerIndex& layerIndex = layerIndexes.at(layerId);
+ layerIndex.addTile(coord, symbolInstances);
+}
+
+void CrossTileSymbolIndex::removeTileLayer(std::string& layerId, const OverscaledTileID& coord) {
+ auto it = layerIndexes.find(layerId);
+ if (it != layerIndexes.end()) {
+ it->second.removeTile(coord);
+ }
+}
+} // namespace mbgl