summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/core-files.cmake5
-rw-r--r--src/mbgl/layout/symbol_layout.cpp568
-rw-r--r--src/mbgl/layout/symbol_layout.hpp127
-rw-r--r--src/mbgl/renderer/bucket.hpp4
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp595
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp143
-rw-r--r--src/mbgl/style/bucket_parameters.hpp3
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.cpp71
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp2
-rw-r--r--src/mbgl/tile/geometry_tile.cpp8
-rw-r--r--src/mbgl/tile/tile_worker.cpp191
-rw-r--r--src/mbgl/tile/tile_worker.hpp41
-rw-r--r--src/mbgl/util/clip_lines.cpp4
-rw-r--r--src/mbgl/util/clip_lines.hpp2
-rw-r--r--src/mbgl/util/merge_lines.hpp2
-rw-r--r--src/mbgl/util/worker.cpp10
-rw-r--r--src/mbgl/util/worker.hpp3
17 files changed, 908 insertions, 871 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index eafe513d22..20aa4dfd0c 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -79,6 +79,10 @@ set(MBGL_CORE_FILES
src/mbgl/gl/object_store.cpp
src/mbgl/gl/object_store.hpp
+ # layout
+ src/mbgl/layout/symbol_layout.cpp
+ src/mbgl/layout/symbol_layout.hpp
+
# map
include/mbgl/map/camera.hpp
include/mbgl/map/map.hpp
@@ -250,6 +254,7 @@ set(MBGL_CORE_FILES
src/mbgl/style/tile_source_impl.cpp
src/mbgl/style/tile_source_impl.hpp
src/mbgl/style/types.cpp
+ src/mbgl/style/update_batch.hpp
src/mbgl/style/update_parameters.hpp
# style/conversion
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
new file mode 100644
index 0000000000..aa22eb428d
--- /dev/null
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -0,0 +1,568 @@
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/sprite/sprite_store.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/text/get_anchors.hpp>
+#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/util/utf.hpp>
+#include <mbgl/util/token.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/merge_lines.hpp>
+#include <mbgl/util/clip_lines.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/math/minmax.hpp>
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line,
+ const Shaping& shapedText, const PositionedIcon& shapedIcon,
+ const SymbolLayoutProperties& layout, const bool addToBuffers, const uint32_t index_,
+ const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement,
+ const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement,
+ const GlyphPositions& face, const IndexedSubfeature& indexedFeature) :
+ point(anchor.point),
+ index(index_),
+ hasText(shapedText),
+ hasIcon(shapedIcon),
+
+ // Create the quads used for rendering the glyphs.
+ glyphQuads(addToBuffers && shapedText ?
+ getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textPlacement, face) :
+ SymbolQuads()),
+
+ // Create the quad used for rendering the icon.
+ iconQuads(addToBuffers && shapedIcon ?
+ getIconQuads(anchor, shapedIcon, line, layout, iconPlacement, shapedText) :
+ SymbolQuads()),
+
+ // Create the collision features that will be used to check whether this symbol instance can be placed
+ textCollisionFeature(line, anchor, shapedText, textBoxScale, textPadding, textPlacement, indexedFeature),
+ iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature)
+ {}
+
+
+SymbolLayout::SymbolLayout(std::string bucketName_,
+ std::string sourceLayerName_,
+ uint32_t overscaling_,
+ float zoom_,
+ const MapMode mode_,
+ const GeometryTileLayer& layer,
+ const style::Filter& filter,
+ style::SymbolLayoutProperties layout_,
+ float textMaxSize_,
+ SpriteAtlas& spriteAtlas_)
+ : bucketName(std::move(bucketName_)),
+ sourceLayerName(std::move(sourceLayerName_)),
+ overscaling(overscaling_),
+ zoom(zoom_),
+ mode(mode_),
+ layout(std::move(layout_)),
+ textMaxSize(textMaxSize_),
+ spriteAtlas(spriteAtlas_),
+ tileSize(util::tileSize * overscaling_),
+ tilePixelRatio(float(util::EXTENT) / tileSize) {
+
+ const bool hasText = !layout.textField.value.empty() && !layout.textFont.value.empty();
+ const bool hasIcon = !layout.iconImage.value.empty();
+
+ if (!hasText && !hasIcon) {
+ return;
+ }
+
+ auto layerName = layer.getName();
+
+ // Determine and load glyph ranges
+ const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
+ for (GLsizei i = 0; i < featureCount; i++) {
+ auto feature = layer.getFeature(i);
+ if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
+ continue;
+
+ SymbolFeature ft;
+ ft.index = i;
+
+ auto getValue = [&feature](const std::string& key) -> std::string {
+ auto value = feature->getValue(key);
+ if (!value)
+ return std::string();
+ if (value->is<std::string>())
+ return value->get<std::string>();
+ if (value->is<bool>())
+ return value->get<bool>() ? "true" : "false";
+ if (value->is<int64_t>())
+ return util::toString(value->get<int64_t>());
+ if (value->is<uint64_t>())
+ return util::toString(value->get<uint64_t>());
+ if (value->is<double>())
+ return util::toString(value->get<double>());
+ return "null";
+ };
+
+ if (hasText) {
+ std::string u8string = util::replaceTokens(layout.textField, getValue);
+
+ if (layout.textTransform == TextTransformType::Uppercase) {
+ u8string = platform::uppercase(u8string);
+ } else if (layout.textTransform == TextTransformType::Lowercase) {
+ u8string = platform::lowercase(u8string);
+ }
+
+ ft.label = util::utf8_to_utf32::convert(u8string);
+
+ if (!ft.label.empty()) {
+ // Loop through all characters of this text and collect unique codepoints.
+ for (char32_t chr : ft.label) {
+ ranges.insert(getGlyphRange(chr));
+ }
+ }
+ }
+
+ if (hasIcon) {
+ ft.sprite = util::replaceTokens(layout.iconImage, getValue);
+ }
+
+ if (ft.label.length() || ft.sprite.length()) {
+
+ auto &multiline = ft.geometry;
+
+ GeometryCollection geometryCollection = feature->getGeometries();
+ for (auto& line : geometryCollection) {
+ multiline.emplace_back();
+ for (auto& point : line) {
+ multiline.back().emplace_back(point.x, point.y);
+ }
+ }
+
+ features.push_back(std::move(ft));
+ }
+ }
+
+ if (layout.symbolPlacement == SymbolPlacementType::Line) {
+ util::mergeLines(features);
+ }
+}
+
+bool SymbolLayout::hasSymbolInstances() const {
+ return !symbolInstances.empty();
+}
+
+bool SymbolLayout::canPrepare(GlyphStore& glyphStore, SpriteStore& spriteStore) {
+ if (!layout.textField.value.empty() && !layout.textFont.value.empty() && !glyphStore.hasGlyphRanges(layout.textFont, ranges)) {
+ return false;
+ }
+
+ if (!layout.iconImage.value.empty() && !spriteStore.isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+void SymbolLayout::prepare(uintptr_t tileUID,
+ GlyphAtlas& glyphAtlas,
+ GlyphStore& glyphStore) {
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (layout.textAnchor) {
+ case TextAnchorType::Top:
+ case TextAnchorType::Bottom:
+ case TextAnchorType::Center:
+ break;
+ case TextAnchorType::Right:
+ case TextAnchorType::TopRight:
+ case TextAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case TextAnchorType::Left:
+ case TextAnchorType::TopLeft:
+ case TextAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (layout.textAnchor) {
+ case TextAnchorType::Left:
+ case TextAnchorType::Right:
+ case TextAnchorType::Center:
+ break;
+ case TextAnchorType::Bottom:
+ case TextAnchorType::BottomLeft:
+ case TextAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case TextAnchorType::Top:
+ case TextAnchorType::TopLeft:
+ case TextAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ const float justify = layout.textJustify == TextJustifyType::Right ? 1 :
+ layout.textJustify == TextJustifyType::Left ? 0 :
+ 0.5;
+
+ auto glyphSet = glyphStore.getGlyphSet(layout.textFont);
+
+ for (const auto& feature : features) {
+ if (feature.geometry.empty()) continue;
+
+ Shaping shapedText;
+ PositionedIcon shapedIcon;
+ GlyphPositions face;
+
+ // if feature has text, shape the text
+ if (feature.label.length()) {
+ shapedText = glyphSet->getShaping(
+ /* string */ feature.label,
+ /* maxWidth: ems */ layout.symbolPlacement != SymbolPlacementType::Line ?
+ layout.textMaxWidth * 24 : 0,
+ /* lineHeight: ems */ layout.textLineHeight * 24,
+ /* horizontalAlign */ horizontalAlign,
+ /* verticalAlign */ verticalAlign,
+ /* justify */ justify,
+ /* spacing: ems */ layout.textLetterSpacing * 24,
+ /* translate */ Point<float>(layout.textOffset.value[0], layout.textOffset.value[1]));
+
+ // Add the glyphs we need for this label to the glyph atlas.
+ if (shapedText) {
+ glyphAtlas.addGlyphs(tileUID, feature.label, layout.textFont, **glyphSet, face);
+ }
+ }
+
+ // if feature has icon, get sprite atlas position
+ if (feature.sprite.length()) {
+ auto image = spriteAtlas.getImage(feature.sprite, SpritePatternMode::Single);
+ if (image) {
+ shapedIcon = shapeIcon(*image, layout);
+ assert((*image).spriteImage);
+ if ((*image).spriteImage->sdf) {
+ sdfIcons = true;
+ }
+ if ((*image).relativePixelRatio != 1.0f) {
+ iconsNeedLinear = true;
+ } else if (layout.iconRotate != 0) {
+ iconsNeedLinear = true;
+ }
+ }
+ }
+
+ // if either shapedText or icon position is present, add the feature
+ if (shapedText || shapedIcon) {
+ addFeature(feature.geometry, shapedText, shapedIcon, face, feature.index);
+ }
+ }
+
+ features.clear();
+}
+
+
+void SymbolLayout::addFeature(const GeometryCollection &lines,
+ const Shaping &shapedText, const PositionedIcon &shapedIcon, const GlyphPositions &face, const size_t index) {
+
+ const float minScale = 0.5f;
+ const float glyphSize = 24.0f;
+
+ const float fontScale = layout.textSize / glyphSize;
+ const float textBoxScale = tilePixelRatio * fontScale;
+ const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize;
+ const float iconBoxScale = tilePixelRatio * layout.iconSize;
+ const float symbolSpacing = tilePixelRatio * layout.symbolSpacing;
+ const bool avoidEdges = layout.symbolAvoidEdges && layout.symbolPlacement != SymbolPlacementType::Line;
+ const float textPadding = layout.textPadding * tilePixelRatio;
+ const float iconPadding = layout.iconPadding * tilePixelRatio;
+ const float textMaxAngle = layout.textMaxAngle * util::DEG2RAD;
+ const SymbolPlacementType textPlacement = layout.textRotationAlignment != AlignmentType::Map
+ ? SymbolPlacementType::Point
+ : layout.symbolPlacement;
+ const SymbolPlacementType iconPlacement = layout.iconRotationAlignment != AlignmentType::Map
+ ? SymbolPlacementType::Point
+ : layout.symbolPlacement;
+ const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
+ layout.textIgnorePlacement || layout.iconIgnorePlacement;
+ const bool isLine = layout.symbolPlacement == SymbolPlacementType::Line;
+ const float textRepeatDistance = symbolSpacing / 2;
+
+ auto& clippedLines = isLine ?
+ util::clipLines(lines, 0, 0, util::EXTENT, util::EXTENT) :
+ lines;
+
+ IndexedSubfeature indexedFeature = {index, sourceLayerName, bucketName, symbolInstances.size()};
+
+ for (const auto& line : clippedLines) {
+ if (line.empty()) continue;
+
+ // Calculate the anchor points around which you want to place labels
+ Anchors anchors = isLine ?
+ getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textMaxBoxScale, overscaling) :
+ Anchors({ Anchor(float(line[0].x), float(line[0].y), 0, minScale) });
+
+ // For each potential label, create the placement features used to check for collisions, and the quads use for rendering.
+ for (Anchor &anchor : anchors) {
+ if (shapedText && isLine) {
+ if (anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
+ continue;
+ }
+ }
+
+ const bool inside = !(anchor.point.x < 0 || anchor.point.x > util::EXTENT || anchor.point.y < 0 || anchor.point.y > util::EXTENT);
+
+ if (avoidEdges && !inside) continue;
+
+ // Normally symbol layers are drawn across tile boundaries. Only symbols
+ // with their anchors within the tile boundaries are added to the buffers
+ // to prevent symbols from being drawn twice.
+ //
+ // Symbols in layers with overlap are sorted in the y direction so that
+ // symbols lower on the canvas are drawn on top of symbols near the top.
+ // To preserve this order across tile boundaries these symbols can't
+ // be drawn across tile boundaries. Instead they need to be included in
+ // the buffers for both tiles and clipped to tile boundaries at draw time.
+ //
+ // TODO remove the `&& false` when is #1673 implemented
+ const bool addToBuffers = (mode == MapMode::Still) || inside || (mayOverlap && false);
+
+ symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers, symbolInstances.size(),
+ textBoxScale, textPadding, textPlacement,
+ iconBoxScale, iconPadding, iconPlacement,
+ face, indexedFeature);
+ }
+ }
+}
+
+bool SymbolLayout::anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor) {
+ if (compareText.find(text) == compareText.end()) {
+ compareText.emplace(text, Anchors());
+ } else {
+ auto otherAnchors = compareText.find(text)->second;
+ for (Anchor &otherAnchor : otherAnchors) {
+ if (util::dist<float>(anchor.point, otherAnchor.point) < repeatDistance) {
+ return true;
+ }
+ }
+ }
+ compareText[text].push_back(anchor);
+ return false;
+}
+
+std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
+ auto bucket = std::make_unique<SymbolBucket>(mode, layout, sdfIcons, iconsNeedLinear);
+
+ // Calculate which labels can be shown and when they can be shown and
+ // create the bufers used for rendering.
+
+ const SymbolPlacementType textPlacement = layout.textRotationAlignment != AlignmentType::Map
+ ? SymbolPlacementType::Point
+ : layout.symbolPlacement;
+ const SymbolPlacementType iconPlacement = layout.iconRotationAlignment != AlignmentType::Map
+ ? SymbolPlacementType::Point
+ : layout.symbolPlacement;
+
+ const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
+ layout.textIgnorePlacement || layout.iconIgnorePlacement;
+
+ // Sort symbols by their y position on the canvas so that they lower symbols
+ // are drawn on top of higher symbols.
+ // Don't sort symbols that won't overlap because it isn't necessary and
+ // because it causes more labels to pop in and out when rotating.
+ if (mayOverlap) {
+ const float sin = std::sin(collisionTile.config.angle);
+ const float cos = std::cos(collisionTile.config.angle);
+
+ std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
+ const int32_t aRotated = sin * a.point.x + cos * a.point.y;
+ const int32_t bRotated = sin * b.point.x + cos * b.point.y;
+ return aRotated != bRotated ?
+ aRotated < bRotated :
+ a.index > b.index;
+ });
+ }
+
+ for (SymbolInstance &symbolInstance : symbolInstances) {
+
+ const bool hasText = symbolInstance.hasText;
+ const bool hasIcon = symbolInstance.hasIcon;
+
+ const bool iconWithoutText = layout.textOptional || !hasText;
+ const bool textWithoutIcon = layout.iconOptional || !hasIcon;
+
+ // Calculate the scales at which the text and icon can be placed without collision.
+
+ float glyphScale = hasText ?
+ collisionTile.placeFeature(symbolInstance.textCollisionFeature,
+ layout.textAllowOverlap, layout.symbolAvoidEdges) :
+ collisionTile.minScale;
+ float iconScale = hasIcon ?
+ collisionTile.placeFeature(symbolInstance.iconCollisionFeature,
+ layout.iconAllowOverlap, layout.symbolAvoidEdges) :
+ collisionTile.minScale;
+
+
+ // Combine the scales for icons and text.
+
+ if (!iconWithoutText && !textWithoutIcon) {
+ iconScale = glyphScale = util::max(iconScale, glyphScale);
+ } else if (!textWithoutIcon && glyphScale) {
+ glyphScale = util::max(iconScale, glyphScale);
+ } else if (!iconWithoutText && iconScale) {
+ iconScale = util::max(iconScale, glyphScale);
+ }
+
+
+ // Insert final placement into collision tree and add glyphs/icons to buffers
+
+ if (hasText) {
+ collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.textIgnorePlacement);
+ if (glyphScale < collisionTile.maxScale) {
+ addSymbols<SymbolBucket::TextBuffer, SymbolBucket::TextElementGroup>(
+ bucket->text, symbolInstance.glyphQuads, glyphScale,
+ layout.textKeepUpright, textPlacement, collisionTile.config.angle);
+ }
+ }
+
+ if (hasIcon) {
+ collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.iconIgnorePlacement);
+ if (iconScale < collisionTile.maxScale) {
+ addSymbols<SymbolBucket::IconBuffer, SymbolBucket::IconElementGroup>(
+ bucket->icon, symbolInstance.iconQuads, iconScale,
+ layout.iconKeepUpright, iconPlacement, collisionTile.config.angle);
+ }
+ }
+ }
+
+ if (collisionTile.config.debug) {
+ addToDebugBuffers(collisionTile, *bucket);
+ }
+
+ return bucket;
+}
+
+template <typename Buffer, typename GroupType>
+void SymbolLayout::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const style::SymbolPlacementType placement, const float placementAngle) {
+
+ const float placementZoom = ::fmax(std::log(scale) / std::log(2) + zoom, 0);
+
+ for (const auto& symbol : symbols) {
+ const auto &tl = symbol.tl;
+ const auto &tr = symbol.tr;
+ const auto &bl = symbol.bl;
+ const auto &br = symbol.br;
+ const auto &tex = symbol.tex;
+
+ float minZoom =
+ util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom);
+ float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
+ const auto &anchorPoint = symbol.anchorPoint;
+
+ // drop upside down versions of glyphs
+ const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
+ if (keepUpright && placement == style::SymbolPlacementType::Line &&
+ (a <= M_PI / 2 || a > M_PI * 3 / 2)) {
+ continue;
+ }
+
+ if (maxZoom <= minZoom)
+ continue;
+
+ // Lower min zoom so that while fading out the label
+ // it can be shown outside of collision-free zoom levels
+ if (minZoom == placementZoom) {
+ minZoom = 0;
+ }
+
+ const int glyph_vertex_length = 4;
+
+ if (buffer.groups.empty() || (buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) {
+ // Move to a new group because the old one can't hold the geometry.
+ buffer.groups.emplace_back(std::make_unique<GroupType>());
+ }
+
+ // We're generating triangle fans, so we always start with the first
+ // coordinate in this polygon.
+ assert(buffer.groups.back());
+ auto &triangleGroup = *buffer.groups.back();
+ GLsizei triangleIndex = triangleGroup.vertex_length;
+
+ // Encode angle of glyph
+ uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
+
+ // coordinates (2 triangles)
+ buffer.vertices.add(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom,
+ maxZoom, placementZoom, glyphAngle);
+ buffer.vertices.add(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y,
+ minZoom, maxZoom, placementZoom, glyphAngle);
+ buffer.vertices.add(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h,
+ minZoom, maxZoom, placementZoom, glyphAngle);
+ buffer.vertices.add(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
+ minZoom, maxZoom, placementZoom, glyphAngle);
+
+ // add the two triangles, referencing the four coordinates we just inserted.
+ buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
+ buffer.triangles.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
+
+ triangleGroup.vertex_length += glyph_vertex_length;
+ triangleGroup.elements_length += 2;
+ }
+}
+
+void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
+
+ const float yStretch = collisionTile.yStretch;
+ const float angle = collisionTile.config.angle;
+ float angle_sin = std::sin(-angle);
+ float angle_cos = std::cos(-angle);
+ std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
+
+ for (const SymbolInstance &symbolInstance : symbolInstances) {
+ for (int i = 0; i < 2; i++) {
+ auto& feature = i == 0 ?
+ symbolInstance.textCollisionFeature :
+ symbolInstance.iconCollisionFeature;
+
+ for (const CollisionBox &box : feature.boxes) {
+ auto& anchor = box.anchor;
+
+ Point<float> tl{box.x1, box.y1 * yStretch};
+ Point<float> tr{box.x2, box.y1 * yStretch};
+ Point<float> bl{box.x1, box.y2 * yStretch};
+ Point<float> br{box.x2, box.y2 * yStretch};
+ tl = util::matrixMultiply(matrix, tl);
+ tr = util::matrixMultiply(matrix, tr);
+ bl = util::matrixMultiply(matrix, bl);
+ br = util::matrixMultiply(matrix, br);
+
+ const float maxZoom = util::max(0.0f, util::min(25.0f, static_cast<float>(zoom + log(box.maxScale) / log(2))));
+ const float placementZoom= util::max(0.0f, util::min(25.0f, static_cast<float>(zoom + log(box.placementScale) / log(2))));
+
+ auto& collisionBox = bucket.collisionBox;
+ if (collisionBox.groups.empty()) {
+ // Move to a new group because the old one can't hold the geometry.
+ collisionBox.groups.emplace_back(std::make_unique<SymbolBucket::CollisionBoxElementGroup>());
+ }
+
+ collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, tr.x, tr.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, tr.x, tr.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, br.x, br.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, br.x, br.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, bl.x, bl.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, bl.x, bl.y, maxZoom, placementZoom);
+ collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
+
+ auto &group= *collisionBox.groups.back();
+ group.vertex_length += 8;
+ }
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
new file mode 100644
index 0000000000..83a3735061
--- /dev/null
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/map/mode.hpp>
+#include <mbgl/text/collision_feature.hpp>
+#include <mbgl/text/quads.hpp>
+#include <mbgl/style/layers/symbol_layer_properties.hpp>
+
+#include <memory>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace mbgl {
+
+class CollisionTile;
+class SpriteAtlas;
+class SpriteStore;
+class GlyphAtlas;
+class GlyphStore;
+class IndexedSubfeature;
+class SymbolBucket;
+
+namespace style {
+class Filter;
+} // namespace style
+
+class SymbolFeature {
+public:
+ GeometryCollection geometry;
+ std::u32string label;
+ std::string sprite;
+ std::size_t index;
+};
+
+struct Anchor;
+
+class SymbolInstance {
+public:
+ explicit SymbolInstance(Anchor& anchor, const GeometryCoordinates& line,
+ const Shaping& shapedText, const PositionedIcon& shapedIcon,
+ const style::SymbolLayoutProperties&, const bool inside, const uint32_t index,
+ const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement,
+ const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement,
+ const GlyphPositions& face, const IndexedSubfeature& indexedfeature);
+
+ Point<float> point;
+ uint32_t index;
+ bool hasText;
+ bool hasIcon;
+ SymbolQuads glyphQuads;
+ SymbolQuads iconQuads;
+ CollisionFeature textCollisionFeature;
+ CollisionFeature iconCollisionFeature;
+};
+
+class SymbolLayout {
+public:
+ SymbolLayout(std::string bucketName_,
+ std::string sourceLayerName_,
+ uint32_t overscaling,
+ float zoom,
+ const MapMode,
+ const GeometryTileLayer&,
+ const style::Filter&,
+ style::SymbolLayoutProperties,
+ float textMaxSize,
+ SpriteAtlas&);
+
+ bool canPrepare(GlyphStore&, SpriteStore&);
+
+ void prepare(uintptr_t tileUID,
+ GlyphAtlas&,
+ GlyphStore&);
+
+ std::unique_ptr<SymbolBucket> place(CollisionTile&);
+
+ bool hasSymbolInstances() const;
+
+ 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.
+ };
+
+ State state = Pending;
+
+ const std::string bucketName;
+ const std::string sourceLayerName;
+
+private:
+ void addFeature(const GeometryCollection&,
+ const Shaping& shapedText,
+ const PositionedIcon& shapedIcon,
+ const GlyphPositions& face,
+ const size_t index);
+
+ bool anchorIsTooClose(const std::u32string& text, const float repeatDistance, Anchor&);
+ std::map<std::u32string, std::vector<Anchor>> compareText;
+
+ void addToDebugBuffers(CollisionTile&, SymbolBucket&);
+
+ // Adds placed items to the buffer.
+ template <typename Buffer, typename GroupType>
+ void addSymbols(Buffer&, const SymbolQuads&, float scale,
+ const bool keepUpright, const style::SymbolPlacementType, const float placementAngle);
+
+ const float overscaling;
+ const float zoom;
+ const MapMode mode;
+ const style::SymbolLayoutProperties layout;
+ const float textMaxSize;
+
+ SpriteAtlas& spriteAtlas;
+
+ const uint32_t tileSize;
+ const float tilePixelRatio;
+
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+
+ std::set<GlyphRange> ranges;
+ std::vector<SymbolInstance> symbolInstances;
+ std::vector<SymbolFeature> features;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index cf461e4389..0efcd2845e 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -13,7 +13,6 @@ namespace mbgl {
class Painter;
class PaintParameters;
-class CollisionTile;
class RenderTile;
namespace gl {
@@ -47,9 +46,6 @@ public:
return !uploaded;
}
- virtual void placeFeatures(CollisionTile&) {}
- virtual void swapRenderData() {}
-
protected:
std::atomic<bool> uploaded { false };
};
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index 9d0cb14609..39fcb91e0c 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -1,86 +1,32 @@
#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/sprite/sprite_image.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/geometry/text_buffer.hpp>
-#include <mbgl/geometry/icon_buffer.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/geometry/anchor.hpp>
-#include <mbgl/text/get_anchors.hpp>
#include <mbgl/renderer/painter.hpp>
-#include <mbgl/text/glyph_store.hpp>
-#include <mbgl/text/glyph_set.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/shader/collision_box_shader.hpp>
-#include <mbgl/util/utf.hpp>
-#include <mbgl/util/token.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/merge_lines.hpp>
-#include <mbgl/util/clip_lines.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/math/minmax.hpp>
-
namespace mbgl {
using namespace style;
-SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line,
- const Shaping& shapedText, const PositionedIcon& shapedIcon,
- const SymbolLayoutProperties& layout, const bool addToBuffers, const uint32_t index_,
- const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement,
- const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement,
- const GlyphPositions& face, const IndexedSubfeature& indexedFeature) :
- point(anchor.point),
- index(index_),
- hasText(shapedText),
- hasIcon(shapedIcon),
-
- // Create the quads used for rendering the glyphs.
- glyphQuads(addToBuffers && shapedText ?
- getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textPlacement, face) :
- SymbolQuads()),
-
- // Create the quad used for rendering the icon.
- iconQuads(addToBuffers && shapedIcon ?
- getIconQuads(anchor, shapedIcon, line, layout, iconPlacement, shapedText) :
- SymbolQuads()),
-
- // Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line, anchor, shapedText, textBoxScale, textPadding, textPlacement, indexedFeature),
- iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature)
- {}
-
-
-SymbolBucket::SymbolBucket(uint32_t overscaling_, float zoom_, const MapMode mode_, std::string bucketName_, std::string sourceLayerName_)
- : overscaling(overscaling_),
- zoom(zoom_),
- tileSize(util::tileSize * overscaling_),
- tilePixelRatio(float(util::EXTENT) / tileSize),
- mode(mode_),
- bucketName(std::move(bucketName_)),
- sourceLayerName(std::move(sourceLayerName_)) {}
-
-SymbolBucket::~SymbolBucket() {
- // Do not remove. header file only contains forward definitions to unique pointers.
+SymbolBucket::SymbolBucket(const MapMode mode_,
+ style::SymbolLayoutProperties layout_,
+ bool sdfIcons_,
+ bool iconsNeedLinear_)
+ : mode(mode_),
+ layout(std::move(layout_)),
+ sdfIcons(sdfIcons_),
+ iconsNeedLinear(iconsNeedLinear_) {
}
void SymbolBucket::upload(gl::ObjectStore& store, gl::Config&) {
if (hasTextData()) {
- renderData->text.vertices.upload(store);
- renderData->text.triangles.upload(store);
+ text.vertices.upload(store);
+ text.triangles.upload(store);
}
if (hasIconData()) {
- renderData->icon.vertices.upload(store);
- renderData->icon.triangles.upload(store);
+ icon.vertices.upload(store);
+ icon.triangles.upload(store);
}
uploaded = true;
@@ -93,520 +39,29 @@ void SymbolBucket::render(Painter& painter,
painter.renderSymbol(parameters, *this, *layer.as<SymbolLayer>(), tile);
}
-bool SymbolBucket::hasData() const { return hasTextData() || hasIconData() || !symbolInstances.empty(); }
-
-bool SymbolBucket::hasTextData() const { return renderData && !renderData->text.groups.empty(); }
-
-bool SymbolBucket::hasIconData() const { return renderData && !renderData->icon.groups.empty(); }
-
-bool SymbolBucket::hasCollisionBoxData() const { return renderData && !renderData->collisionBox.groups.empty(); }
-
-bool SymbolBucket::needsClipping() const {
- return mode == MapMode::Still;
-}
-
-void SymbolBucket::parseFeatures(const GeometryTileLayer& layer, const Filter& filter) {
- const bool has_text = !layout.textField.value.empty() && !layout.textFont.value.empty();
- const bool has_icon = !layout.iconImage.value.empty();
-
- if (!has_text && !has_icon) {
- return;
- }
-
- auto layerName = layer.getName();
-
- // Determine and load glyph ranges
- const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
- for (GLsizei i = 0; i < featureCount; i++) {
- auto feature = layer.getFeature(i);
- if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
- continue;
-
- SymbolFeature ft;
- ft.index = i;
-
- auto getValue = [&feature](const std::string& key) -> std::string {
- auto value = feature->getValue(key);
- if (!value)
- return std::string();
- if (value->is<std::string>())
- return value->get<std::string>();
- if (value->is<bool>())
- return value->get<bool>() ? "true" : "false";
- if (value->is<int64_t>())
- return util::toString(value->get<int64_t>());
- if (value->is<uint64_t>())
- return util::toString(value->get<uint64_t>());
- if (value->is<double>())
- return util::toString(value->get<double>());
- return "null";
- };
-
- if (has_text) {
- std::string u8string = util::replaceTokens(layout.textField, getValue);
-
- if (layout.textTransform == TextTransformType::Uppercase) {
- u8string = platform::uppercase(u8string);
- } else if (layout.textTransform == TextTransformType::Lowercase) {
- u8string = platform::lowercase(u8string);
- }
-
- ft.label = util::utf8_to_utf32::convert(u8string);
-
- if (!ft.label.empty()) {
- // Loop through all characters of this text and collect unique codepoints.
- for (char32_t chr : ft.label) {
- ranges.insert(getGlyphRange(chr));
- }
- }
- }
-
- if (has_icon) {
- ft.sprite = util::replaceTokens(layout.iconImage, getValue);
- }
-
- if (ft.label.length() || ft.sprite.length()) {
-
- auto &multiline = ft.geometry;
-
- GeometryCollection geometryCollection = feature->getGeometries();
- for (auto& line : geometryCollection) {
- multiline.emplace_back();
- for (auto& point : line) {
- multiline.back().emplace_back(point.x, point.y);
- }
- }
-
- features.push_back(std::move(ft));
- }
- }
-
- if (layout.symbolPlacement == SymbolPlacementType::Line) {
- util::mergeLines(features);
- }
-}
-
-bool SymbolBucket::needsDependencies(GlyphStore& glyphStore, SpriteStore& spriteStore) {
- if (!layout.textField.value.empty() && !layout.textFont.value.empty() && !glyphStore.hasGlyphRanges(layout.textFont, ranges)) {
- return true;
- }
-
- if (!layout.iconImage.value.empty() && !spriteStore.isLoaded()) {
- return true;
- }
-
- return false;
-}
-
-void SymbolBucket::addFeatures(uintptr_t tileUID,
- SpriteAtlas& spriteAtlas,
- GlyphAtlas& glyphAtlas,
- GlyphStore& glyphStore) {
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
-
- switch (layout.textAnchor) {
- case TextAnchorType::Top:
- case TextAnchorType::Bottom:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Right:
- case TextAnchorType::TopRight:
- case TextAnchorType::BottomRight:
- horizontalAlign = 1;
- break;
- case TextAnchorType::Left:
- case TextAnchorType::TopLeft:
- case TextAnchorType::BottomLeft:
- horizontalAlign = 0;
- break;
- }
-
- switch (layout.textAnchor) {
- case TextAnchorType::Left:
- case TextAnchorType::Right:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Bottom:
- case TextAnchorType::BottomLeft:
- case TextAnchorType::BottomRight:
- verticalAlign = 1;
- break;
- case TextAnchorType::Top:
- case TextAnchorType::TopLeft:
- case TextAnchorType::TopRight:
- verticalAlign = 0;
- break;
- }
-
- const float justify = layout.textJustify == TextJustifyType::Right ? 1 :
- layout.textJustify == TextJustifyType::Left ? 0 :
- 0.5;
-
- auto glyphSet = glyphStore.getGlyphSet(layout.textFont);
-
- for (const auto& feature : features) {
- if (feature.geometry.empty()) continue;
-
- Shaping shapedText;
- PositionedIcon shapedIcon;
- GlyphPositions face;
-
- // if feature has text, shape the text
- if (feature.label.length()) {
- shapedText = glyphSet->getShaping(
- /* string */ feature.label,
- /* maxWidth: ems */ layout.symbolPlacement != SymbolPlacementType::Line ?
- layout.textMaxWidth * 24 : 0,
- /* lineHeight: ems */ layout.textLineHeight * 24,
- /* horizontalAlign */ horizontalAlign,
- /* verticalAlign */ verticalAlign,
- /* justify */ justify,
- /* spacing: ems */ layout.textLetterSpacing * 24,
- /* translate */ Point<float>(layout.textOffset.value[0], layout.textOffset.value[1]));
-
- // Add the glyphs we need for this label to the glyph atlas.
- if (shapedText) {
- glyphAtlas.addGlyphs(tileUID, feature.label, layout.textFont, **glyphSet, face);
- }
- }
-
- // if feature has icon, get sprite atlas position
- if (feature.sprite.length()) {
- auto image = spriteAtlas.getImage(feature.sprite, SpritePatternMode::Single);
- if (image) {
- shapedIcon = shapeIcon(*image, layout);
- assert((*image).spriteImage);
- if ((*image).spriteImage->sdf) {
- sdfIcons = true;
- }
- if ((*image).relativePixelRatio != 1.0f) {
- iconsNeedLinear = true;
- } else if (layout.iconRotate != 0) {
- iconsNeedLinear = true;
- }
- }
- }
-
- // if either shapedText or icon position is present, add the feature
- if (shapedText || shapedIcon) {
- addFeature(feature.geometry, shapedText, shapedIcon, face, feature.index);
- }
- }
-
- features.clear();
-}
-
-
-void SymbolBucket::addFeature(const GeometryCollection &lines,
- const Shaping &shapedText, const PositionedIcon &shapedIcon, const GlyphPositions &face, const size_t index) {
-
- const float minScale = 0.5f;
- const float glyphSize = 24.0f;
-
- const float fontScale = layout.textSize / glyphSize;
- const float textBoxScale = tilePixelRatio * fontScale;
- const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize;
- const float iconBoxScale = tilePixelRatio * layout.iconSize;
- const float symbolSpacing = tilePixelRatio * layout.symbolSpacing;
- const bool avoidEdges = layout.symbolAvoidEdges && layout.symbolPlacement != SymbolPlacementType::Line;
- const float textPadding = layout.textPadding * tilePixelRatio;
- const float iconPadding = layout.iconPadding * tilePixelRatio;
- const float textMaxAngle = layout.textMaxAngle * util::DEG2RAD;
- const SymbolPlacementType textPlacement = layout.textRotationAlignment != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.symbolPlacement;
- const SymbolPlacementType iconPlacement = layout.iconRotationAlignment != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.symbolPlacement;
- const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
- layout.textIgnorePlacement || layout.iconIgnorePlacement;
- const bool isLine = layout.symbolPlacement == SymbolPlacementType::Line;
- const float textRepeatDistance = symbolSpacing / 2;
-
- auto& clippedLines = isLine ?
- util::clipLines(lines, 0, 0, util::EXTENT, util::EXTENT) :
- lines;
-
- IndexedSubfeature indexedFeature = {index, sourceLayerName, bucketName, symbolInstances.size()};
-
- for (const auto& line : clippedLines) {
- if (line.empty()) continue;
-
- // Calculate the anchor points around which you want to place labels
- Anchors anchors = isLine ?
- getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textMaxBoxScale, overscaling) :
- Anchors({ Anchor(float(line[0].x), float(line[0].y), 0, minScale) });
-
- // For each potential label, create the placement features used to check for collisions, and the quads use for rendering.
- for (Anchor &anchor : anchors) {
- if (shapedText && isLine) {
- if (anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
- continue;
- }
- }
-
- const bool inside = !(anchor.point.x < 0 || anchor.point.x > util::EXTENT || anchor.point.y < 0 || anchor.point.y > util::EXTENT);
-
- if (avoidEdges && !inside) continue;
-
- // Normally symbol layers are drawn across tile boundaries. Only symbols
- // with their anchors within the tile boundaries are added to the buffers
- // to prevent symbols from being drawn twice.
- //
- // Symbols in layers with overlap are sorted in the y direction so that
- // symbols lower on the canvas are drawn on top of symbols near the top.
- // To preserve this order across tile boundaries these symbols can't
- // be drawn across tile boundaries. Instead they need to be included in
- // the buffers for both tiles and clipped to tile boundaries at draw time.
- //
- // TODO remove the `&& false` when is #1673 implemented
- const bool addToBuffers = (mode == MapMode::Still) || inside || (mayOverlap && false);
-
- symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers, symbolInstances.size(),
- textBoxScale, textPadding, textPlacement,
- iconBoxScale, iconPadding, iconPlacement,
- face, indexedFeature);
- }
- }
-}
-
-bool SymbolBucket::anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor) {
- if (compareText.find(text) == compareText.end()) {
- compareText.emplace(text, Anchors());
- } else {
- auto otherAnchors = compareText.find(text)->second;
- for (Anchor &otherAnchor : otherAnchors) {
- if (util::dist<float>(anchor.point, otherAnchor.point) < repeatDistance) {
- return true;
- }
- }
- }
- compareText[text].push_back(anchor);
- return false;
+bool SymbolBucket::hasData() const {
+ return hasTextData() || hasIconData();
}
-void SymbolBucket::placeFeatures(CollisionTile& collisionTile) {
-
- renderDataInProgress = std::make_unique<SymbolRenderData>();
-
- // Calculate which labels can be shown and when they can be shown and
- // create the bufers used for rendering.
-
- const SymbolPlacementType textPlacement = layout.textRotationAlignment != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.symbolPlacement;
- const SymbolPlacementType iconPlacement = layout.iconRotationAlignment != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.symbolPlacement;
-
- const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
- layout.textIgnorePlacement || layout.iconIgnorePlacement;
-
- // Sort symbols by their y position on the canvas so that they lower symbols
- // are drawn on top of higher symbols.
- // Don't sort symbols that won't overlap because it isn't necessary and
- // because it causes more labels to pop in and out when rotating.
- if (mayOverlap) {
- const float sin = std::sin(collisionTile.config.angle);
- const float cos = std::cos(collisionTile.config.angle);
-
- std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
- const int32_t aRotated = sin * a.point.x + cos * a.point.y;
- const int32_t bRotated = sin * b.point.x + cos * b.point.y;
- return aRotated != bRotated ?
- aRotated < bRotated :
- a.index > b.index;
- });
- }
-
- for (SymbolInstance &symbolInstance : symbolInstances) {
-
- const bool hasText = symbolInstance.hasText;
- const bool hasIcon = symbolInstance.hasIcon;
-
- const bool iconWithoutText = layout.textOptional || !hasText;
- const bool textWithoutIcon = layout.iconOptional || !hasIcon;
-
- // Calculate the scales at which the text and icon can be placed without collision.
-
- float glyphScale = hasText ?
- collisionTile.placeFeature(symbolInstance.textCollisionFeature,
- layout.textAllowOverlap, layout.symbolAvoidEdges) :
- collisionTile.minScale;
- float iconScale = hasIcon ?
- collisionTile.placeFeature(symbolInstance.iconCollisionFeature,
- layout.iconAllowOverlap, layout.symbolAvoidEdges) :
- collisionTile.minScale;
-
-
- // Combine the scales for icons and text.
-
- if (!iconWithoutText && !textWithoutIcon) {
- iconScale = glyphScale = util::max(iconScale, glyphScale);
- } else if (!textWithoutIcon && glyphScale) {
- glyphScale = util::max(iconScale, glyphScale);
- } else if (!iconWithoutText && iconScale) {
- iconScale = util::max(iconScale, glyphScale);
- }
-
-
- // Insert final placement into collision tree and add glyphs/icons to buffers
-
- if (hasText) {
- collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.textIgnorePlacement);
- if (glyphScale < collisionTile.maxScale) {
- addSymbols<SymbolRenderData::TextBuffer, TextElementGroup>(
- renderDataInProgress->text, symbolInstance.glyphQuads, glyphScale,
- layout.textKeepUpright, textPlacement, collisionTile.config.angle);
- }
- }
-
- if (hasIcon) {
- collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.iconIgnorePlacement);
- if (iconScale < collisionTile.maxScale) {
- addSymbols<SymbolRenderData::IconBuffer, IconElementGroup>(
- renderDataInProgress->icon, symbolInstance.iconQuads, iconScale,
- layout.iconKeepUpright, iconPlacement, collisionTile.config.angle);
- }
- }
- }
-
- if (collisionTile.config.debug) {
- addToDebugBuffers(collisionTile);
- }
+bool SymbolBucket::hasTextData() const {
+ return !text.groups.empty();
}
-template <typename Buffer, typename GroupType>
-void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const style::SymbolPlacementType placement, const float placementAngle) {
-
- const float placementZoom = ::fmax(std::log(scale) / std::log(2) + zoom, 0);
-
- for (const auto& symbol : symbols) {
- const auto &tl = symbol.tl;
- const auto &tr = symbol.tr;
- const auto &bl = symbol.bl;
- const auto &br = symbol.br;
- const auto &tex = symbol.tex;
-
- float minZoom =
- util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom);
- float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
- const auto &anchorPoint = symbol.anchorPoint;
-
- // drop upside down versions of glyphs
- const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
- if (keepUpright && placement == style::SymbolPlacementType::Line &&
- (a <= M_PI / 2 || a > M_PI * 3 / 2)) {
- continue;
- }
-
- if (maxZoom <= minZoom)
- continue;
-
- // Lower min zoom so that while fading out the label
- // it can be shown outside of collision-free zoom levels
- if (minZoom == placementZoom) {
- minZoom = 0;
- }
-
- const int glyph_vertex_length = 4;
-
- if (buffer.groups.empty() || (buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) {
- // Move to a new group because the old one can't hold the geometry.
- buffer.groups.emplace_back(std::make_unique<GroupType>());
- }
-
- // We're generating triangle fans, so we always start with the first
- // coordinate in this polygon.
- assert(buffer.groups.back());
- auto &triangleGroup = *buffer.groups.back();
- GLsizei triangleIndex = triangleGroup.vertex_length;
-
- // Encode angle of glyph
- uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
-
- // coordinates (2 triangles)
- buffer.vertices.add(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom,
- maxZoom, placementZoom, glyphAngle);
- buffer.vertices.add(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle);
- buffer.vertices.add(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle);
- buffer.vertices.add(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle);
-
- // add the two triangles, referencing the four coordinates we just inserted.
- buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
- buffer.triangles.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
-
- triangleGroup.vertex_length += glyph_vertex_length;
- triangleGroup.elements_length += 2;
- }
+bool SymbolBucket::hasIconData() const {
+ return !icon.groups.empty();
}
-void SymbolBucket::addToDebugBuffers(CollisionTile &collisionTile) {
-
- const float yStretch = collisionTile.yStretch;
- const float angle = collisionTile.config.angle;
- float angle_sin = std::sin(-angle);
- float angle_cos = std::cos(-angle);
- std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
-
- for (const SymbolInstance &symbolInstance : symbolInstances) {
- for (int i = 0; i < 2; i++) {
- auto& feature = i == 0 ?
- symbolInstance.textCollisionFeature :
- symbolInstance.iconCollisionFeature;
-
- for (const CollisionBox &box : feature.boxes) {
- auto& anchor = box.anchor;
-
- Point<float> tl{box.x1, box.y1 * yStretch};
- Point<float> tr{box.x2, box.y1 * yStretch};
- Point<float> bl{box.x1, box.y2 * yStretch};
- Point<float> br{box.x2, box.y2 * yStretch};
- tl = util::matrixMultiply(matrix, tl);
- tr = util::matrixMultiply(matrix, tr);
- bl = util::matrixMultiply(matrix, bl);
- br = util::matrixMultiply(matrix, br);
-
- const float maxZoom = util::max(0.0f, util::min(25.0f, static_cast<float>(zoom + log(box.maxScale) / log(2))));
- const float placementZoom= util::max(0.0f, util::min(25.0f, static_cast<float>(zoom + log(box.placementScale) / log(2))));
-
- auto& collisionBox = renderDataInProgress->collisionBox;
- if (collisionBox.groups.empty()) {
- // Move to a new group because the old one can't hold the geometry.
- collisionBox.groups.emplace_back(std::make_unique<CollisionBoxElementGroup>());
- }
-
- collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, tr.x, tr.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, tr.x, tr.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, br.x, br.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, br.x, br.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, bl.x, bl.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, bl.x, bl.y, maxZoom, placementZoom);
- collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
-
- auto &group= *collisionBox.groups.back();
- group.vertex_length += 8;
- }
- }
- }
+bool SymbolBucket::hasCollisionBoxData() const {
+ return !collisionBox.groups.empty();
}
-void SymbolBucket::swapRenderData() {
- if (renderDataInProgress) {
- renderData = std::move(renderDataInProgress);
- uploaded = false;
- }
+bool SymbolBucket::needsClipping() const {
+ return mode == MapMode::Still;
}
void SymbolBucket::drawGlyphs(SDFShader& shader, gl::ObjectStore& store, PaintMode paintMode) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
- auto& text = renderData->text;
for (auto &group : text.groups) {
assert(group);
group->array[paintMode == PaintMode::Overdraw ? 1 : 0].bind(
@@ -621,7 +76,6 @@ void SymbolBucket::drawGlyphs(SDFShader& shader, gl::ObjectStore& store, PaintMo
void SymbolBucket::drawIcons(SDFShader& shader, gl::ObjectStore& store, PaintMode paintMode) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
- auto& icon = renderData->icon;
for (auto &group : icon.groups) {
assert(group);
group->array[paintMode == PaintMode::Overdraw ? 1 : 0].bind(
@@ -636,7 +90,6 @@ void SymbolBucket::drawIcons(SDFShader& shader, gl::ObjectStore& store, PaintMod
void SymbolBucket::drawIcons(IconShader& shader, gl::ObjectStore& store, PaintMode paintMode) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
- auto& icon = renderData->icon;
for (auto &group : icon.groups) {
assert(group);
group->array[paintMode == PaintMode::Overdraw ? 3 : 2].bind(
@@ -650,10 +103,10 @@ void SymbolBucket::drawIcons(IconShader& shader, gl::ObjectStore& store, PaintMo
void SymbolBucket::drawCollisionBoxes(CollisionBoxShader& shader, gl::ObjectStore& store) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
- auto& collisionBox = renderData->collisionBox;
for (auto &group : collisionBox.groups) {
group->array[0].bind(shader, collisionBox.vertices, vertex_index, store);
MBGL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, group->vertex_length));
}
}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index 8c76bd8045..8f0c21bc95 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -1,22 +1,15 @@
#pragma once
#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/map/mode.hpp>
-#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/text_buffer.hpp>
#include <mbgl/geometry/icon_buffer.hpp>
#include <mbgl/geometry/collision_box_buffer.hpp>
-#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/collision_feature.hpp>
-#include <mbgl/text/shaping.hpp>
-#include <mbgl/text/quads.hpp>
-#include <mbgl/style/filter.hpp>
+#include <mbgl/text/glyph_range.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <memory>
-#include <map>
#include <set>
#include <vector>
@@ -25,49 +18,13 @@ namespace mbgl {
class SDFShader;
class IconShader;
class CollisionBoxShader;
-class CollisionTile;
-class SpriteAtlas;
-class SpriteStore;
-class GlyphAtlas;
-class GlyphStore;
-class IndexedSubfeature;
-
-class SymbolFeature {
-public:
- GeometryCollection geometry;
- std::u32string label;
- std::string sprite;
- std::size_t index;
-};
-
-struct Anchor;
-
-class SymbolInstance {
- public:
- explicit SymbolInstance(Anchor& anchor, const GeometryCoordinates& line,
- const Shaping& shapedText, const PositionedIcon& shapedIcon,
- const style::SymbolLayoutProperties&, const bool inside, const uint32_t index,
- const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement,
- const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement,
- const GlyphPositions& face, const IndexedSubfeature& indexedfeature);
- Point<float> point;
- uint32_t index;
- bool hasText;
- bool hasIcon;
- SymbolQuads glyphQuads;
- SymbolQuads iconQuads;
- CollisionFeature textCollisionFeature;
- CollisionFeature iconCollisionFeature;
-};
class SymbolBucket : public Bucket {
- typedef ElementGroup<2> TextElementGroup;
- typedef ElementGroup<4> IconElementGroup;
- typedef ElementGroup<1> CollisionBoxElementGroup;
-
public:
- SymbolBucket(uint32_t overscaling, float zoom, const MapMode, std::string bucketName_, std::string sourceLayerName_);
- ~SymbolBucket() override;
+ SymbolBucket(const MapMode,
+ style::SymbolLayoutProperties,
+ bool sdfIcons,
+ bool iconsNeedLinear);
void upload(gl::ObjectStore&, gl::Config&) override;
void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
@@ -77,81 +34,39 @@ public:
bool hasCollisionBoxData() const;
bool needsClipping() const override;
- void addFeatures(uintptr_t tileUID,
- SpriteAtlas&,
- GlyphAtlas&,
- GlyphStore&);
-
void drawGlyphs(SDFShader&, gl::ObjectStore&, PaintMode);
void drawIcons(SDFShader&, gl::ObjectStore&, PaintMode);
void drawIcons(IconShader&, gl::ObjectStore&, PaintMode);
void drawCollisionBoxes(CollisionBoxShader&, gl::ObjectStore&);
- void parseFeatures(const GeometryTileLayer&, const style::Filter&);
- bool needsDependencies(GlyphStore&, SpriteStore&);
- void placeFeatures(CollisionTile&) override;
-
-private:
- void addFeature(const GeometryCollection &lines,
- const Shaping &shapedText, const PositionedIcon &shapedIcon,
- const GlyphPositions &face,
- const size_t index);
- bool anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor);
- std::map<std::u32string, std::vector<Anchor>> compareText;
-
- void addToDebugBuffers(CollisionTile &collisionTile);
-
- void swapRenderData() override;
-
- // Adds placed items to the buffer.
- template <typename Buffer, typename GroupType>
- void addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale,
- const bool keepUpright, const style::SymbolPlacementType placement, const float placementAngle);
-
-public:
- style::SymbolLayoutProperties layout;
-
- float iconMaxSize = 1.0f;
- float textMaxSize = 16.0f;
-
- bool sdfIcons = false;
- bool iconsNeedLinear = false;
-
-private:
-
- const float overscaling;
- const float zoom;
- const uint32_t tileSize;
- const float tilePixelRatio;
const MapMode mode;
- const std::string bucketName;
- const std::string sourceLayerName;
-
- std::set<GlyphRange> ranges;
- std::vector<SymbolInstance> symbolInstances;
- std::vector<SymbolFeature> features;
+ const style::SymbolLayoutProperties layout;
+ const bool sdfIcons;
+ const bool iconsNeedLinear;
- struct SymbolRenderData {
- struct TextBuffer {
- TextVertexBuffer vertices;
- TriangleElementsBuffer triangles;
- std::vector<std::unique_ptr<TextElementGroup>> groups;
- } text;
-
- struct IconBuffer {
- IconVertexBuffer vertices;
- TriangleElementsBuffer triangles;
- std::vector<std::unique_ptr<IconElementGroup>> groups;
- } icon;
+private:
+ friend class SymbolLayout;
- struct CollisionBoxBuffer {
- CollisionBoxVertexBuffer vertices;
- std::vector<std::unique_ptr<CollisionBoxElementGroup>> groups;
- } collisionBox;
- };
+ typedef ElementGroup<2> TextElementGroup;
+ typedef ElementGroup<4> IconElementGroup;
+ typedef ElementGroup<1> CollisionBoxElementGroup;
- std::unique_ptr<SymbolRenderData> renderData;
- std::unique_ptr<SymbolRenderData> renderDataInProgress;
+ struct TextBuffer {
+ TextVertexBuffer vertices;
+ TriangleElementsBuffer triangles;
+ std::vector<std::unique_ptr<TextElementGroup>> groups;
+ } text;
+
+ struct IconBuffer {
+ IconVertexBuffer vertices;
+ TriangleElementsBuffer triangles;
+ std::vector<std::unique_ptr<IconElementGroup>> groups;
+ } icon;
+
+ struct CollisionBoxBuffer {
+ CollisionBoxVertexBuffer vertices;
+ std::vector<std::unique_ptr<CollisionBoxElementGroup>> groups;
+ } collisionBox;
};
} // namespace mbgl
diff --git a/src/mbgl/style/bucket_parameters.hpp b/src/mbgl/style/bucket_parameters.hpp
index 3b42e7c41b..ccd5f9a0e4 100644
--- a/src/mbgl/style/bucket_parameters.hpp
+++ b/src/mbgl/style/bucket_parameters.hpp
@@ -26,7 +26,6 @@ public:
const GeometryTileLayer& layer_,
const std::atomic<bool>& obsolete_,
uintptr_t tileUID_,
- bool& partialParse_,
SpriteStore& spriteStore_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
@@ -36,7 +35,6 @@ public:
layer(layer_),
obsolete(obsolete_),
tileUID(tileUID_),
- partialParse(partialParse_),
spriteStore(spriteStore_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
@@ -53,7 +51,6 @@ public:
const GeometryTileLayer& layer;
const std::atomic<bool>& obsolete;
uintptr_t tileUID;
- bool& partialParse;
SpriteStore& spriteStore;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp
index 0243b1dfa5..1039951f5e 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.cpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.cpp
@@ -1,6 +1,7 @@
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/style/bucket_parameters.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/renderer/bucket.hpp>
namespace mbgl {
namespace style {
@@ -25,54 +26,44 @@ bool SymbolLayer::Impl::recalculate(const CalculationParameters& parameters) {
return hasTransitions;
}
-std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(BucketParameters& parameters) const {
- auto bucket = std::make_unique<SymbolBucket>(parameters.tileID.overscaleFactor(),
- parameters.tileID.overscaledZ,
- parameters.mode,
- id,
- parameters.layer.getName());
+std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(BucketParameters&) const {
+ assert(false); // Should be calling createLayout() instead.
+ return nullptr;
+}
- bucket->layout = layout;
+std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(BucketParameters& parameters) const {
+ SymbolLayoutProperties layoutProperties = layout;
CalculationParameters p(parameters.tileID.overscaledZ);
- bucket->layout.symbolPlacement.calculate(p);
- if (bucket->layout.symbolPlacement.value == SymbolPlacementType::Line) {
- bucket->layout.iconRotationAlignment.value = AlignmentType::Map;
- bucket->layout.textRotationAlignment.value = AlignmentType::Map;
- };
+ layoutProperties.symbolPlacement.calculate(p);
+ if (layoutProperties.symbolPlacement.value == SymbolPlacementType::Line) {
+ layoutProperties.iconRotationAlignment.value = AlignmentType::Map;
+ layoutProperties.textRotationAlignment.value = AlignmentType::Map;
+ }
// If unspecified `text-pitch-alignment` inherits `text-rotation-alignment`
- if (bucket->layout.textPitchAlignment.value == AlignmentType::Undefined) {
- bucket->layout.textPitchAlignment.value = bucket->layout.textRotationAlignment.value;
- };
-
- bucket->layout.recalculate(p);
-
- bucket->layout.iconSize.calculate(CalculationParameters(18));
- bucket->layout.textSize.calculate(CalculationParameters(18));
- bucket->iconMaxSize = bucket->layout.iconSize;
- bucket->textMaxSize = bucket->layout.textSize;
- bucket->layout.iconSize.calculate(CalculationParameters(p.z + 1));
- bucket->layout.textSize.calculate(CalculationParameters(p.z + 1));
+ if (layoutProperties.textPitchAlignment.value == AlignmentType::Undefined) {
+ layoutProperties.textPitchAlignment.value = layoutProperties.textRotationAlignment.value;
+ }
- bucket->parseFeatures(parameters.layer, filter);
+ layoutProperties.recalculate(p);
- if (bucket->needsDependencies(parameters.glyphStore, parameters.spriteStore)) {
- parameters.partialParse = true;
- }
+ layoutProperties.textSize.calculate(CalculationParameters(18));
+ float textMaxSize = layoutProperties.textSize;
- // We do not add features if the parser is in a "partial" state because
- // the layer ordering needs to be respected when calculating text
- // collisions. Although, at this point, we requested all the resources
- // needed by this tile.
- if (!parameters.partialParse) {
- bucket->addFeatures(parameters.tileUID,
- *spriteAtlas,
- parameters.glyphAtlas,
- parameters.glyphStore);
- }
+ layoutProperties.iconSize.calculate(CalculationParameters(p.z + 1));
+ layoutProperties.textSize.calculate(CalculationParameters(p.z + 1));
- return std::move(bucket);
+ return std::make_unique<SymbolLayout>(id,
+ parameters.layer.getName(),
+ parameters.tileID.overscaleFactor(),
+ parameters.tileID.overscaledZ,
+ parameters.mode,
+ parameters.layer,
+ filter,
+ layoutProperties,
+ textMaxSize,
+ *spriteAtlas);
}
} // namespace style
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index 7765d6790e..fe37ba86ea 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -7,6 +7,7 @@
namespace mbgl {
class SpriteAtlas;
+class SymbolLayout;
namespace style {
@@ -19,6 +20,7 @@ public:
bool recalculate(const CalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(BucketParameters&) const override;
+ std::unique_ptr<SymbolLayout> createLayout(BucketParameters&) const;
SymbolLayoutProperties layout;
SymbolPaintProperties paint;
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index f74f813e48..c432869fa5 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -193,19 +193,19 @@ void GeometryTile::redoPlacement() {
return;
}
- workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, config = targetConfig](std::unique_ptr<CollisionTile> collisionTile) {
+ workRequest = worker.redoPlacement(tileWorker, targetConfig, [this, config = targetConfig](TilePlacementResult result) {
workRequest.reset();
// Persist the configuration we just placed so that we can later check whether we need to
// place again in case the configuration has changed.
placedConfig = config;
- for (auto& bucket : buckets) {
- bucket.second->swapRenderData();
+ for (auto& bucket : result.buckets) {
+ buckets[bucket.first] = std::move(bucket.second);
}
if (featureIndex) {
- featureIndex->setCollisionTile(std::move(collisionTile));
+ featureIndex->setCollisionTile(std::move(result.collisionTile));
}
// The target configuration could have changed since we started placement. In this case,
diff --git a/src/mbgl/tile/tile_worker.cpp b/src/mbgl/tile/tile_worker.cpp
index d5e551d118..572f9054cd 100644
--- a/src/mbgl/tile/tile_worker.cpp
+++ b/src/mbgl/tile/tile_worker.cpp
@@ -1,6 +1,7 @@
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/tile/tile_worker.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/style/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -12,7 +13,7 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/exception.hpp>
-#include <utility>
+#include <unordered_set>
namespace mbgl {
@@ -38,149 +39,141 @@ TileWorker::~TileWorker() {
TileParseResult TileWorker::parseAllLayers(std::vector<std::unique_ptr<Layer>> layers_,
std::unique_ptr<const GeometryTileData> tileData_,
- PlacementConfig config) {
+ const PlacementConfig& config) {
tileData = std::move(tileData_);
return redoLayout(std::move(layers_), config);
}
TileParseResult TileWorker::redoLayout(std::vector<std::unique_ptr<Layer>> layers_,
- const PlacementConfig config) {
+ const PlacementConfig& config) {
layers = std::move(layers_);
// We're doing a fresh parse of the tile, because the underlying data or style has changed.
- pending.clear();
- placementPending.clear();
- partialParse = false;
featureIndex = std::make_unique<FeatureIndex>();
+ symbolLayouts.clear();
// We're storing a set of bucket names we've parsed to avoid parsing a bucket twice that is
// referenced from more than one layer
- std::set<std::string> parsed;
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ std::unordered_set<std::string> parsed;
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
- const Layer* layer = i->get();
- if (parsed.find(layer->baseImpl->bucketName()) == parsed.end()) {
- parsed.emplace(layer->baseImpl->bucketName());
- parseLayer(layer);
+ if (obsolete) {
+ break;
}
- featureIndex->addBucketLayerName(layer->baseImpl->bucketName(), layer->baseImpl->id);
- }
- return prepareResult(config);
-}
+ // Temporary prevention for crashing due to https://github.com/mapbox/mapbox-gl-native/issues/6263.
+ // Instead, the race condition will produce a blank tile.
+ if (!tileData) {
+ break;
+ }
-TileParseResult TileWorker::parsePendingLayers(const PlacementConfig config) {
- // Try parsing the remaining layers that we couldn't parse in the first step due to missing
- // dependencies.
- for (auto it = pending.begin(); it != pending.end();) {
- const SymbolLayer& symbolLayer = *it->first;
- SymbolBucket* symbolBucket = dynamic_cast<SymbolBucket*>(it->second.get());
+ const Layer* layer = i->get();
+ const std::string& bucketName = layer->baseImpl->bucketName();
- if (!symbolBucket->needsDependencies(glyphStore, spriteStore)) {
- symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this),
- *symbolLayer.impl->spriteAtlas,
- glyphAtlas,
- glyphStore);
- placementPending.emplace(symbolLayer.impl->bucketName(), std::move(it->second));
- pending.erase(it++);
+ featureIndex->addBucketLayerName(bucketName, layer->baseImpl->id);
+
+ if (parsed.find(bucketName) != parsed.end()) {
+ continue;
+ }
+
+ parsed.emplace(bucketName);
+
+ auto geometryLayer = tileData->getLayer(layer->baseImpl->sourceLayer);
+ if (!geometryLayer) {
continue;
}
- // Advance the iterator here; we're skipping this when erasing an element from this list.
- ++it;
+ BucketParameters parameters(id,
+ *geometryLayer,
+ obsolete,
+ reinterpret_cast<uintptr_t>(this),
+ spriteStore,
+ glyphAtlas,
+ glyphStore,
+ *featureIndex,
+ mode);
+
+ if (layer->is<SymbolLayer>()) {
+ symbolLayouts.push_back(layer->as<SymbolLayer>()->impl->createLayout(parameters));
+ } else {
+ std::unique_ptr<Bucket> bucket = layer->baseImpl->createBucket(parameters);
+ if (bucket->hasData()) {
+ buckets.emplace(layer->baseImpl->bucketName(), std::move(bucket));
+ }
+ }
}
- return prepareResult(config);
+ return parsePendingLayers(config, std::move(buckets));
}
-TileParseResult TileWorker::prepareResult(const PlacementConfig& config) {
- result.complete = pending.empty();
+TileParseResult TileWorker::parsePendingLayers(const PlacementConfig& config) {
+ return parsePendingLayers(config, std::unordered_map<std::string, std::unique_ptr<Bucket>>());
+}
- if (result.complete) {
- featureIndex->setCollisionTile(placeLayers(config));
- result.featureIndex = std::move(featureIndex);
- result.tileData = tileData ? tileData->clone() : nullptr;
- }
+TileParseResult TileWorker::parsePendingLayers(const PlacementConfig& config,
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets) {
+ TileParseResultData result;
- return std::move(result);
-}
+ result.complete = true;
+ result.buckets = std::move(buckets);
-std::unique_ptr<CollisionTile> TileWorker::placeLayers(const PlacementConfig config) {
- auto collisionTile = redoPlacement(&placementPending, config);
- for (auto &p : placementPending) {
- p.second->swapRenderData();
- insertBucket(p.first, std::move(p.second));
+ // Prepare as many SymbolLayouts as possible.
+ for (auto& symbolLayout : symbolLayouts) {
+ if (symbolLayout->state == SymbolLayout::Pending) {
+ if (symbolLayout->canPrepare(glyphStore, spriteStore)) {
+ symbolLayout->state = SymbolLayout::Prepared;
+ symbolLayout->prepare(reinterpret_cast<uintptr_t>(this),
+ glyphAtlas,
+ glyphStore);
+ } else {
+ result.complete = false;
+ }
+ }
}
- placementPending.clear();
- return collisionTile;
-}
-std::unique_ptr<CollisionTile> TileWorker::redoPlacement(
- const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
- PlacementConfig config) {
+ // If all SymbolLayouts are prepared, then perform placement. Otherwise, parsePendingLayers
+ // will eventually be re-run.
+ if (result.complete) {
+ TilePlacementResult placementResult = redoPlacement(config);
- auto collisionTile = std::make_unique<CollisionTile>(config);
+ featureIndex->setCollisionTile(std::move(placementResult.collisionTile));
- for (auto i = layers.rbegin(); i != layers.rend(); i++) {
- const auto it = buckets->find((*i)->baseImpl->id);
- if (it != buckets->end()) {
- it->second->placeFeatures(*collisionTile);
+ for (auto& bucket : placementResult.buckets) {
+ result.buckets.emplace(std::move(bucket));
+ }
+
+ result.featureIndex = std::move(featureIndex);
+
+ if (tileData) {
+ result.tileData = tileData->clone();
}
}
- return collisionTile;
+ return std::move(result);
}
-void TileWorker::parseLayer(const Layer* layer) {
- // Cancel early when parsing.
- if (obsolete)
- return;
+TilePlacementResult TileWorker::redoPlacement(const PlacementConfig& config) {
+ TilePlacementResult result;
- // Temporary prevention for crashing due to https://github.com/mapbox/mapbox-gl-native/issues/6263.
- // Instead, the race condition will produce a blank tile.
- if (!tileData) {
- return;
- }
+ result.collisionTile = std::make_unique<CollisionTile>(config);
- auto geometryLayer = tileData->getLayer(layer->baseImpl->sourceLayer);
- if (!geometryLayer) {
- // The layer specified in the bucket does not exist. Do nothing.
- if (debug::tileParseWarnings) {
- Log::Warning(Event::ParseTile, "layer '%s' does not exist in tile %s",
- layer->baseImpl->sourceLayer.c_str(), util::toString(id).c_str());
+ for (auto& symbolLayout : symbolLayouts) {
+ if (symbolLayout->state == SymbolLayout::Pending) {
+ // Can't do placement until all layouts are prepared.
+ return result;
}
- return;
}
- BucketParameters parameters(id,
- *geometryLayer,
- obsolete,
- reinterpret_cast<uintptr_t>(this),
- partialParse,
- spriteStore,
- glyphAtlas,
- glyphStore,
- *featureIndex,
- mode);
-
- std::unique_ptr<Bucket> bucket = layer->baseImpl->createBucket(parameters);
-
- if (layer->is<SymbolLayer>()) {
- if (partialParse) {
- // We cannot parse this bucket yet. Instead, we're saving it for later.
- pending.emplace_back(layer->as<SymbolLayer>(), std::move(bucket));
- } else {
- placementPending.emplace(layer->baseImpl->bucketName(), std::move(bucket));
+ for (auto& symbolLayout : symbolLayouts) {
+ symbolLayout->state = SymbolLayout::Placed;
+ std::unique_ptr<Bucket> bucket = symbolLayout->place(*result.collisionTile);
+ if (bucket->hasData() || symbolLayout->hasSymbolInstances()) {
+ result.buckets.emplace(symbolLayout->bucketName, std::move(bucket));
}
- } else {
- insertBucket(layer->baseImpl->bucketName(), std::move(bucket));
}
-}
-void TileWorker::insertBucket(const std::string& name, std::unique_ptr<Bucket> bucket) {
- if (bucket->hasData()) {
- result.buckets.emplace(name, std::move(bucket));
- }
+ return result;
}
} // namespace mbgl
diff --git a/src/mbgl/tile/tile_worker.hpp b/src/mbgl/tile/tile_worker.hpp
index 5f38c898ff..e64e7dee19 100644
--- a/src/mbgl/tile/tile_worker.hpp
+++ b/src/mbgl/tile/tile_worker.hpp
@@ -4,7 +4,6 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/variant.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <atomic>
@@ -22,10 +21,11 @@ class SpriteStore;
class GlyphAtlas;
class GlyphStore;
class Bucket;
+class SymbolLayout;
+class PlacementConfig;
namespace style {
class Layer;
-class SymbolLayer;
} // namespace style
// We're using this class to shuttle the resulting buckets from the worker thread to the MapContext
@@ -42,6 +42,12 @@ using TileParseResult = variant<
TileParseResultData, // success
std::exception_ptr>; // error
+class TilePlacementResult {
+public:
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ std::unique_ptr<CollisionTile> collisionTile;
+};
+
class TileWorker : public util::noncopyable {
public:
TileWorker(OverscaledTileID,
@@ -54,21 +60,18 @@ public:
TileParseResult parseAllLayers(std::vector<std::unique_ptr<style::Layer>>,
std::unique_ptr<const GeometryTileData>,
- PlacementConfig);
+ const PlacementConfig&);
- TileParseResult parsePendingLayers(PlacementConfig);
+ TileParseResult parsePendingLayers(const PlacementConfig&);
TileParseResult redoLayout(std::vector<std::unique_ptr<style::Layer>>,
- PlacementConfig);
+ const PlacementConfig&);
- std::unique_ptr<CollisionTile> redoPlacement(const std::unordered_map<std::string, std::unique_ptr<Bucket>>*,
- PlacementConfig);
+ TilePlacementResult redoPlacement(const PlacementConfig&);
private:
- TileParseResult prepareResult(const PlacementConfig& config);
- void parseLayer(const style::Layer*);
- void insertBucket(const std::string& name, std::unique_ptr<Bucket>);
- std::unique_ptr<CollisionTile> placeLayers(PlacementConfig);
+ TileParseResult parsePendingLayers(const PlacementConfig&,
+ std::unordered_map<std::string, std::unique_ptr<Bucket>>);
const OverscaledTileID id;
@@ -78,23 +81,11 @@ private:
const std::atomic<bool>& obsolete;
const MapMode mode;
- bool partialParse = false;
-
std::vector<std::unique_ptr<style::Layer>> layers;
-
- std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<const GeometryTileData> tileData;
- // Contains buckets that we couldn't parse so far due to missing resources.
- // They will be attempted on subsequent parses.
- std::list<std::pair<const style::SymbolLayer*, std::unique_ptr<Bucket>>> pending;
-
- // Contains buckets that have been parsed, but still need placement.
- // They will be placed when all buckets have been parsed.
- std::unordered_map<std::string, std::unique_ptr<Bucket>> placementPending;
-
- // Temporary holder
- TileParseResultData result;
+ std::unique_ptr<FeatureIndex> featureIndex;
+ std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
};
} // namespace mbgl
diff --git a/src/mbgl/util/clip_lines.cpp b/src/mbgl/util/clip_lines.cpp
index 41965876cb..81021f4b5b 100644
--- a/src/mbgl/util/clip_lines.cpp
+++ b/src/mbgl/util/clip_lines.cpp
@@ -1,4 +1,6 @@
-#include "clip_lines.hpp"
+#include <mbgl/util/clip_lines.hpp>
+
+#include <cmath>
namespace mbgl {
namespace util {
diff --git a/src/mbgl/util/clip_lines.hpp b/src/mbgl/util/clip_lines.hpp
index 19ab3ac201..c1cfd4bbb9 100644
--- a/src/mbgl/util/clip_lines.hpp
+++ b/src/mbgl/util/clip_lines.hpp
@@ -3,7 +3,7 @@
#include <map>
#include <string>
#include <vector>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
namespace util {
diff --git a/src/mbgl/util/merge_lines.hpp b/src/mbgl/util/merge_lines.hpp
index b1f216148d..8db8c284cd 100644
--- a/src/mbgl/util/merge_lines.hpp
+++ b/src/mbgl/util/merge_lines.hpp
@@ -3,7 +3,7 @@
#include <map>
#include <string>
#include <vector>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
namespace mbgl {
namespace util {
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index d62029acc3..8245628e84 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -63,10 +63,9 @@ public:
}
void redoPlacement(TileWorker* worker,
- const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
PlacementConfig config,
- std::function<void(std::unique_ptr<CollisionTile>)> callback) {
- callback(worker->redoPlacement(buckets, config));
+ std::function<void(TilePlacementResult)> callback) {
+ callback(worker->redoPlacement(config));
}
};
@@ -120,12 +119,11 @@ Worker::redoLayout(TileWorker& worker,
std::unique_ptr<AsyncRequest>
Worker::redoPlacement(TileWorker& worker,
- const std::unordered_map<std::string, std::unique_ptr<Bucket>>& buckets,
PlacementConfig config,
- std::function<void(std::unique_ptr<CollisionTile>)> callback) {
+ std::function<void(TilePlacementResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, &worker,
- &buckets, config, callback);
+ config, callback);
}
} // end namespace mbgl
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 31c41debf2..5b2ea06525 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -55,9 +55,8 @@ public:
std::function<void(TileParseResult)> callback);
Request redoPlacement(TileWorker&,
- const std::unordered_map<std::string, std::unique_ptr<Bucket>>&,
PlacementConfig config,
- std::function<void(std::unique_ptr<CollisionTile>)> callback);
+ std::function<void(TilePlacementResult)> callback);
private:
class Impl;