summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMolly Lloyd <molly@mapbox.com>2018-12-18 15:52:38 -0800
committerMolly Lloyd <molly@mapbox.com>2018-12-18 15:52:38 -0800
commite6bdd4235589dfbaf692d2fbfd8706736aa31060 (patch)
tree2689c3fab87ec9d3e7dd973fc247d6f3fef2cc2f
parent7db9ce37e648e8145816f11dcdb3251f0ce81d74 (diff)
downloadqtlocation-mapboxgl-e6bdd4235589dfbaf692d2fbfd8706736aa31060.tar.gz
implement dynamic-text-anchor property
-rw-r--r--include/mbgl/style/types.hpp4
-rw-r--r--include/mbgl/util/constants.hpp1
m---------mapbox-gl-js0
-rw-r--r--src/mbgl/layout/symbol_instance.cpp33
-rw-r--r--src/mbgl/layout/symbol_instance.hpp13
-rw-r--r--src/mbgl/layout/symbol_layout.cpp83
-rw-r--r--src/mbgl/layout/symbol_layout.hpp2
-rw-r--r--src/mbgl/layout/symbol_projection.hpp8
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp6
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp21
-rw-r--r--src/mbgl/text/collision_index.cpp10
-rw-r--r--src/mbgl/text/collision_index.hpp2
-rw-r--r--src/mbgl/text/placement.cpp146
-rw-r--r--src/mbgl/text/placement.hpp11
-rw-r--r--src/mbgl/text/quads.cpp5
-rw-r--r--src/mbgl/text/shaping.cpp54
-rw-r--r--src/mbgl/text/shaping.hpp50
18 files changed, 330 insertions, 124 deletions
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 2a7635ff2d..19fc2f115f 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -99,7 +99,6 @@ enum class SymbolAnchorType : uint8_t {
};
enum class DynamicTextAnchorType : uint8_t {
- Auto,
Center,
Left,
Right,
@@ -108,7 +107,8 @@ enum class DynamicTextAnchorType : uint8_t {
TopLeft,
TopRight,
BottomLeft,
- BottomRight
+ BottomRight,
+ Auto
};
enum class TextTransformType : uint8_t {
diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp
index 7110d9e26b..6ac9280fba 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -39,6 +39,7 @@ constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
constexpr float MAX_ZOOM_F = MAX_ZOOM;
constexpr uint8_t DEFAULT_MAX_ZOOM = 22;
+constexpr float ONE_EM = 24.0f;
constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4;
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 8e267fe230dbafc75914e437d26efab2f81adf1
+Subproject 825d4ee5add1c2028a39141e8d4d5cfe7014022
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 139a42113c..c4cc04578a 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -7,10 +7,10 @@ using namespace style;
SymbolInstance::SymbolInstance(Anchor& anchor_,
GeometryCoordinates line_,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const std::tuple<Shaping, Shaping, Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
- const float layoutTextSize,
+ const float layoutTextSize_,
const float textBoxScale,
const float textPadding,
const SymbolPlacementType textPlacement,
@@ -31,32 +31,39 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
hasIcon(shapedIcon),
// Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling, rotate),
+ textCollisionFeature(line_, anchor, std::get<0>(shapedTextOrientations), textBoxScale, textPadding, textPlacement, indexedFeature, overscaling, rotate),
iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, rotate),
layoutFeatureIndex(layoutFeatureIndex_),
dataFeatureIndex(dataFeatureIndex_),
textOffset(textOffset_),
iconOffset(iconOffset_),
- key(key_) {
+ key(key_),
+ layoutTextSize(layoutTextSize_) {
// Create the quads used for rendering the icon and glyphs.
if (shapedIcon) {
- iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, std::get<0>(shapedTextOrientations));
}
- if (shapedTextOrientations.first) {
- horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
+ if (std::get<0>(shapedTextOrientations)) {
+ rightJustifiedGlyphQuads = getGlyphQuads(std::get<0>(shapedTextOrientations), layout, textPlacement, positions);
}
- if (shapedTextOrientations.second) {
- verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
+ if (std::get<1>(shapedTextOrientations)) {
+ centerJustifiedGlyphQuads = getGlyphQuads(std::get<1>(shapedTextOrientations), layout, textPlacement, positions);
+ }
+ if (std::get<2>(shapedTextOrientations)) {
+ leftJustifiedGlyphQuads = getGlyphQuads(std::get<2>(shapedTextOrientations), layout, textPlacement, positions);
+ }
+ if (std::get<3>(shapedTextOrientations)) {
+ verticalGlyphQuads = getGlyphQuads(std::get<3>(shapedTextOrientations), layout, textPlacement, positions);
}
// 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
- hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0;
+ hasText = rightJustifiedGlyphQuads.size() > 0 || centerJustifiedGlyphQuads.size() > 0 || leftJustifiedGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0;
- if (shapedTextOrientations.first && shapedTextOrientations.second) {
+ if (std::get<0>(shapedTextOrientations) && std::get<3>(shapedTextOrientations)) {
writingModes = WritingModeType::Horizontal | WritingModeType::Vertical;
- } else if (shapedTextOrientations.first) {
+ } else if (std::get<0>(shapedTextOrientations)) {
writingModes = WritingModeType::Horizontal;
- } else if (shapedTextOrientations.second) {
+ } else if (std::get<3>(shapedTextOrientations)) {
writingModes = WritingModeType::Vertical;
} else {
writingModes = WritingModeType::None;
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index 6148d7fe88..a4d688f4d5 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -15,7 +15,9 @@ class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
GeometryCoordinates line,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ // When dynamic-text-anchor is used, order is right, center, left, vertical.
+ // Otherwise order is horizontal, empty, empty, vertical.
+ const std::tuple<Shaping, Shaping, Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
@@ -38,7 +40,9 @@ public:
GeometryCoordinates line;
bool hasText;
bool hasIcon;
- SymbolQuads horizontalGlyphQuads;
+ SymbolQuads rightJustifiedGlyphQuads;
+ SymbolQuads leftJustifiedGlyphQuads;
+ SymbolQuads centerJustifiedGlyphQuads;
SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
CollisionFeature textCollisionFeature;
@@ -49,8 +53,11 @@ public:
std::array<float, 2> textOffset;
std::array<float, 2> iconOffset;
std::u16string key;
+ float layoutTextSize;
bool isDuplicate;
- optional<size_t> placedTextIndex;
+ optional<size_t> placedRightTextIndex;
+ optional<size_t> placedCenterTextIndex;
+ optional<size_t> placedLeftTextIndex;
optional<size_t> placedVerticalTextIndex;
optional<size_t> placedIconIndex;
uint32_t crossTileID = 0;
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 332fb3f46a..fc086f0f1d 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -180,35 +180,42 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
auto& feature = *it;
if (feature.geometry.empty()) continue;
- std::pair<Shaping, Shaping> shapedTextOrientations;
+ std::tuple<Shaping, Shaping, Shaping, Shaping> shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
-
+ const SymbolAnchorType textAnchor = layout.evaluate<TextAnchor>(zoom, feature);
+ const TextJustifyType textJustify = layout.evaluate<TextJustify>(zoom, feature);
// if feature has text, shape the text
if (feature.formattedText) {
- auto applyShaping = [&] (const TaggedString& formattedText, WritingModeType writingMode) {
- const float oneEm = 24.0f;
+ auto applyShaping = [&] (const TaggedString& formattedText, WritingModeType writingMode, const SymbolAnchorType anchor, const TextJustifyType justify) {
const Shaping result = getShaping(
/* string */ formattedText,
/* maxWidth: ems */ layout.get<SymbolPlacement>() == SymbolPlacementType::Point ?
- layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
- /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
- /* anchor */ layout.evaluate<TextAnchor>(zoom, feature),
- /* justify */ layout.evaluate<TextJustify>(zoom, feature),
- /* spacing: ems */ util::i18n::allowsLetterSpacing(feature.formattedText->rawText()) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f,
- /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
- /* verticalHeight */ oneEm,
+ layout.evaluate<TextMaxWidth>(zoom, feature) * util::ONE_EM : 0,
+ /* lineHeight: ems */ layout.get<TextLineHeight>() * util::ONE_EM,
+ /* anchor */ anchor,
+ /* justify */ justify,
+ /* spacing: ems */ util::i18n::allowsLetterSpacing(feature.formattedText->rawText()) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * util::ONE_EM : 0.0f,
+ /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM, layout.evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM),
+ /* verticalHeight */ util::ONE_EM,
/* writingMode */ writingMode,
/* bidirectional algorithm object */ bidi,
/* glyphs */ glyphMap);
return result;
};
+
+ if (layout.evaluate<DynamicTextAnchor>(zoom, feature).size() > 0 && !textAlongLine) {
+ std::get<0>(shapedTextOrientations) = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::TopLeft, TextJustifyType::Right);
+ std::get<1>(shapedTextOrientations) = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::TopLeft, TextJustifyType::Center);
+ std::get<1>(shapedTextOrientations) = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::TopLeft, TextJustifyType::Left);
+ } else {
+ std::get<0>(shapedTextOrientations) = applyShaping(*feature.formattedText, WritingModeType::Horizontal, textAnchor, textJustify);
+ }
- shapedTextOrientations.first = applyShaping(*feature.formattedText, WritingModeType::Horizontal);
if (util::i18n::allowsVerticalWritingMode(feature.formattedText->rawText()) && textAlongLine) {
feature.formattedText->verticalizePunctuation();
- shapedTextOrientations.second = applyShaping(*feature.formattedText, WritingModeType::Vertical);
+ std::get<3>(shapedTextOrientations) = applyShaping(*feature.formattedText, WritingModeType::Vertical, textAnchor, textJustify);
}
}
@@ -233,7 +240,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
}
// if either shapedText or icon position is present, add the feature
- if (shapedTextOrientations.first || shapedIcon) {
+ if (std::get<0>(shapedTextOrientations) || shapedIcon) {
addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions);
}
@@ -245,7 +252,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const SymbolFeature& feature,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const std::tuple<Shaping, Shaping, Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const GlyphPositions& glyphPositions) {
const float minScale = 0.5f;
@@ -304,8 +311,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
Anchors anchors = getAnchors(line,
symbolSpacing,
textMaxAngle,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).left,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).right,
+ (std::get<3>(shapedTextOrientations) ?: std::get<0>(shapedTextOrientations)).left,
+ (std::get<3>(shapedTextOrientations) ?: std::get<0>(shapedTextOrientations)).right,
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
glyphSize,
@@ -325,8 +332,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
if (line.size() > 1) {
optional<Anchor> anchor = getCenterAnchor(line,
textMaxAngle,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).left,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).right,
+ (std::get<3>(shapedTextOrientations) ?: std::get<0>(shapedTextOrientations)).left,
+ (std::get<3>(shapedTextOrientations) ?: std::get<0>(shapedTextOrientations)).right,
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
glyphSize,
@@ -410,7 +417,7 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
const bool sortFeaturesByY = zOrderByViewport && (layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>());
- auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances));
+ auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, sortFeaturesByY, tilePixelRatio, bucketLeaderID, std::move(symbolInstances));
for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
@@ -423,21 +430,25 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
if (hasText) {
const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
- bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
- symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
- symbolInstance.placedTextIndex = bucket->text.placedSymbols.size() - 1;
- PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back();
-
- bool firstHorizontal = true;
- for (const auto& symbol : symbolInstance.horizontalGlyphQuads) {
- size_t index = addSymbol(
- bucket->text, sizeData, symbol,
- symbolInstance.anchor, horizontalSymbol);
- if (firstHorizontal) {
- horizontalSymbol.vertexStartIndex = index;
- firstHorizontal = false;
+ auto placeSymbol = [&] (SymbolQuads glyphQuads) {
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
+
+ PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back();
+
+ bool firstHorizontal = true;
+ for (const auto& symbol : glyphQuads) {
+ size_t index = addSymbol(
+ bucket->text, sizeData, symbol,
+ symbolInstance.anchor, horizontalSymbol);
+ if (firstHorizontal) {
+ horizontalSymbol.vertexStartIndex = index;
+ firstHorizontal = false;
+ }
}
- }
+ return bucket->text.placedSymbols.size() - 1;
+ };
+
+ symbolInstance.placedRightTextIndex = placeSymbol(symbolInstance.rightJustifiedGlyphQuads);
if (symbolInstance.writingModes & WritingModeType::Vertical) {
bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
@@ -458,6 +469,10 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
}
}
}
+ // didn't have time to figure out why the order matters here,
+ // but if I place these before the vertical text the glyphs don't render - merp
+ symbolInstance.placedCenterTextIndex = placeSymbol(symbolInstance.centerJustifiedGlyphQuads);
+ symbolInstance.placedLeftTextIndex = placeSymbol(symbolInstance.leftJustifiedGlyphQuads);
}
if (hasIcon) {
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 7045eebc22..2365e82aab 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -53,7 +53,7 @@ public:
private:
void addFeature(const size_t,
const SymbolFeature&,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const std::tuple<Shaping, Shaping, Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const GlyphPositions&);
diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp
index 3e57d162fd..ea7fec7d32 100644
--- a/src/mbgl/layout/symbol_projection.hpp
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -13,7 +13,13 @@ namespace mbgl {
namespace style {
class SymbolPropertyValues;
} // end namespace style
-
+
+ void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray);
+ void addDynamicAttributes(
+ const Point<float>& anchorPoint,
+ const float angle,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray
+ );
struct TileDistance {
TileDistance(float prevTileDistance_, float lastSegmentViewportDistance_)
: prevTileDistance(prevTileDistance_), lastSegmentViewportDistance(lastSegmentViewportDistance_)
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index a3f652fc6e..a6fdcb7371 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -18,6 +18,7 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
bool sdfIcons_,
bool iconsNeedLinear_,
bool sortFeaturesByY_,
+ const float tilePixelRatio_,
const std::string bucketName_,
const std::vector<SymbolInstance>&& symbolInstances_)
: Bucket(LayerType::Symbol),
@@ -25,6 +26,7 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
sortFeaturesByY(sortFeaturesByY_),
+ tilePixelRatio(tilePixelRatio_),
bucketLeaderID(std::move(bucketName_)),
symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
@@ -210,8 +212,8 @@ void SymbolBucket::sortFeatures(const float angle) {
const SymbolInstance& symbolInstance = symbolInstances[i];
featureSortOrder->push_back(symbolInstance.dataFeatureIndex);
- if (symbolInstance.placedTextIndex) {
- addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
+ if (symbolInstance.placedRightTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedRightTextIndex]);
}
if (symbolInstance.placedVerticalTextIndex) {
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index 5addff40b2..cfcfceff93 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -20,11 +20,12 @@ class PlacedSymbol {
public:
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
- anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
+ anchorPoint(anchorPoint_), dynamicShift(0, 0), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
{
}
Point<float> anchorPoint;
+ Point<float> dynamicShift;
uint16_t segment;
float lowerSize;
float upperSize;
@@ -47,6 +48,7 @@ public:
bool sdfIcons,
bool iconsNeedLinear,
bool sortFeaturesByY,
+ const float tilePixelRatio,
const std::string bucketLeaderID,
const std::vector<SymbolInstance>&&);
@@ -64,6 +66,7 @@ public:
const bool sdfIcons;
const bool iconsNeedLinear;
const bool sortFeaturesByY;
+ const float tilePixelRatio;
const std::string bucketLeaderID;
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 11ffd74b10..6da536e27b 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -229,6 +229,27 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
parameters.state);
parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
+ } else if (layout.get<DynamicTextAnchor>().size()) {
+ auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(parameters.state.getZoom());
+ bucket.text.dynamicVertices.clear();
+ for (auto& symbol : bucket.text.placedSymbols) {
+ if (symbol.hidden || symbol.dynamicShift.x == -INFINITY) {
+ // These symbols are from a justification that is not being used, or a label that wasn't placed
+ // so we don't need to do the extra math to figure out what incremental shift to apply.
+ hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
+ } else {
+ auto renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol);
+ auto renderTimeTextScale = renderTextSize * bucket.tilePixelRatio / 24;
+
+ const float shiftX = (symbol.dynamicShift.x * renderTimeTextScale) / pow(2, parameters.state.getZoom() - tile.tile.id.overscaledZ);
+ const float shiftY = (symbol.dynamicShift.y * renderTimeTextScale) / pow(2, parameters.state.getZoom() - tile.tile.id.overscaledZ);
+ const Point<float> shiftedAnchor = Point<float>(symbol.anchorPoint.x + shiftX, symbol.anchorPoint.y + shiftY);
+ for (size_t i = 0; i < symbol.glyphOffsets.size(); i++) {
+ addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices);
+ }
+ }
+ }
+ parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
}
const Size texsize = geometryTile.glyphAtlasTexture->size;
diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp
index 90acb2b441..f81a8a4f0f 100644
--- a/src/mbgl/text/collision_index.cpp
+++ b/src/mbgl/text/collision_index.cpp
@@ -80,6 +80,8 @@ bool CollisionIndex::isInsideTile(const CollisionBox& box, const CollisionTileBo
std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
+ const float shiftX,
+ const float shiftY,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
@@ -95,10 +97,10 @@ std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
CollisionBox& box = feature.boxes.front();
const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
const float tileToViewport = textPixelRatio * projectedPoint.second;
- box.px1 = box.x1 * tileToViewport + projectedPoint.first.x;
- box.py1 = box.y1 * tileToViewport + projectedPoint.first.y;
- box.px2 = box.x2 * tileToViewport + projectedPoint.first.x;
- box.py2 = box.y2 * tileToViewport + projectedPoint.first.y;
+ box.px1 = (box.x1 + shiftX) * tileToViewport + projectedPoint.first.x;
+ box.py1 = (box.y1 + shiftY) * tileToViewport + projectedPoint.first.y;
+ box.px2 = (box.x2 + shiftX) * tileToViewport + projectedPoint.first.x;
+ box.py2 = (box.y2 + shiftY) * tileToViewport + projectedPoint.first.y;
if ((avoidEdges && !isInsideTile(box, *avoidEdges)) ||
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
index dac0aa0bf7..c33f4b2199 100644
--- a/src/mbgl/text/collision_index.hpp
+++ b/src/mbgl/text/collision_index.hpp
@@ -23,6 +23,8 @@ public:
explicit CollisionIndex(const TransformState&);
std::pair<bool,bool> placeFeature(CollisionFeature& feature,
+ const float shiftX,
+ const float shiftY,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index a39106a43d..8227a01e2b 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -22,6 +22,54 @@ bool OpacityState::isHidden() const {
return opacity == 0 && !placed;
}
+DynamicTextOffsets::DynamicTextOffsets(Point<float> right_, Point<float> center_, Point<float> left_):
+ right(right_),
+ center(center_),
+ left(left_) {}
+
+const std::vector<style::DynamicTextAnchorType> AUTO_DYNAMIC_TEXT_ANCHORS = std::vector<style::DynamicTextAnchorType> {
+ style::DynamicTextAnchorType::Center,
+ style::DynamicTextAnchorType::Top,
+ style::DynamicTextAnchorType::Bottom,
+ style::DynamicTextAnchorType::Left,
+ style::DynamicTextAnchorType::Right,
+ style::DynamicTextAnchorType::TopLeft,
+ style::DynamicTextAnchorType::TopRight,
+ style::DynamicTextAnchorType::BottomLeft,
+ style::DynamicTextAnchorType::BottomRight
+};
+
+style::TextJustifyType getAnchorJustification(const style::DynamicTextAnchorType anchor) {
+ switch (anchor) {
+ case style::DynamicTextAnchorType::Right:
+ case style::DynamicTextAnchorType::BottomRight:
+ case style::DynamicTextAnchorType::TopRight:
+ return style::TextJustifyType::Right;
+ break;
+
+ case style::DynamicTextAnchorType::Left :
+ case style::DynamicTextAnchorType::TopLeft :
+ case style::DynamicTextAnchorType::BottomLeft :
+ return style::TextJustifyType::Left;
+ break;
+
+ default:
+ return style::TextJustifyType::Center;
+ break;
+ }
+};
+
+void hideUnplacedJustifications(SymbolBucket& bucket, style::TextJustifyType placedJustification, std::map<style::TextJustifyType, optional<size_t>>& placedSymbols) {
+ for (auto const& symbol : placedSymbols ) {
+ if (symbol.first != placedJustification) {
+ if (symbol.second) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbol.second);
+ placedSymbol.dynamicShift = {-INFINITY, -INFINITY};
+ }
+ }
+ }
+};
+
JointOpacityState::JointOpacityState(bool placedText, bool placedIcon, bool skipFade) :
icon(OpacityState(placedIcon, skipFade)),
text(OpacityState(placedText, skipFade)) {}
@@ -161,7 +209,9 @@ void Placement::placeLayerBucket(
// See https://github.com/mapbox/mapbox-gl-native/issues/12683
const bool alwaysShowText = textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || bucket.layout.get<style::IconOptional>());
const bool alwaysShowIcon = iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || bucket.layout.get<style::TextOptional>());
-
+ const std::vector<style::DynamicTextAnchorType> dynamicTextAnchors = bucket.layout.get<style::DynamicTextAnchor>();
+ const std::vector<style::DynamicTextAnchorType> dynamicTextAnchorOrder = dynamicTextAnchors.size() && dynamicTextAnchors[0] == style::DynamicTextAnchorType::Auto ? AUTO_DYNAMIC_TEXT_ANCHORS : dynamicTextAnchors;
+
for (auto& symbolInstance : bucket.symbolInstances) {
if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
@@ -175,12 +225,13 @@ void Placement::placeLayerBucket(
bool placeText = false;
bool placeIcon = false;
bool offscreen = true;
+ CollisionFeature placedCollisionFeature(symbolInstance.textCollisionFeature);
- if (symbolInstance.placedTextIndex) {
- PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
+ if (!dynamicTextAnchors.size() && symbolInstance.placedRightTextIndex) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
- auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
+ auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature, 0, 0,
posMatrix, textLabelPlaneMatrix, textPixelRatio,
placedSymbol, scale, fontSize,
bucket.layout.get<style::TextAllowOverlap>(),
@@ -188,13 +239,54 @@ void Placement::placeLayerBucket(
showCollisionBoxes, avoidEdges, collisionGroup.second);
placeText = placed.first;
offscreen &= placed.second;
+ } else {
+ const float textBoxScale = symbolInstance.layoutTextSize / util::ONE_EM * bucket.tilePixelRatio;
+ std::map<style::TextJustifyType, optional<size_t>> justifications {
+ {style::TextJustifyType::Right, symbolInstance.placedRightTextIndex},
+ {style::TextJustifyType::Center, symbolInstance.placedCenterTextIndex},
+ {style::TextJustifyType::Left, symbolInstance.placedLeftTextIndex},
+ };
+
+ for (const auto anchor : dynamicTextAnchorOrder) {
+ if (!placeText) {
+ if (symbolInstance.placedIconIndex && anchor == style::DynamicTextAnchorType::Center) continue;
+ // Auto is only valid as the first element in a DynamicTextAnchor which should be replaced with the
+ // AUTO_DYNAMIC_TEXT_ANCHORS value above.
+ if (anchor == style::DynamicTextAnchorType::Auto) continue;
+ style::TextJustifyType justification = getAnchorJustification(anchor);
+ const auto placedIndex = justifications[justification];
+ if (!placedIndex) continue;
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*placedIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+ AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment((style::SymbolAnchorType) anchor);
+ CollisionBox& box = symbolInstance.textCollisionFeature.boxes[0];
+
+ const auto shiftX = -anchorAlign.horizontalAlign * (box.x2 - box.x1);
+ const auto shiftY = -anchorAlign.verticalAlign * (box.y2 - box.y1);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature, shiftX, shiftY,
+ posMatrix, textLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::TextAllowOverlap>(),
+ bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes, avoidEdges, collisionGroup.second);
+ if (placed.first) {
+ placeText = placed.first;
+ offscreen &= placed.second;
+ placedSymbol.dynamicShift = {shiftX/textBoxScale, shiftY/textBoxScale};
+ hideUnplacedJustifications(bucket, justification, justifications);
+ break;
+ }
+ }
+ }
}
+
if (symbolInstance.placedIconIndex) {
PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
- auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
+ auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, 0, 0,
posMatrix, iconLabelPlaneMatrix, textPixelRatio,
placedSymbol, scale, fontSize,
bucket.layout.get<style::IconAllowOverlap>(),
@@ -310,6 +402,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
const bool textAllowOverlap = bucket.layout.get<style::TextAllowOverlap>();
const bool iconAllowOverlap = bucket.layout.get<style::IconAllowOverlap>();
+ const bool dynamicText = bucket.layout.get<style::DynamicTextAnchor>().size();
// If allow-overlap is true, we can show symbols before placement runs on them
// But we have to wait for placement if we potentially depend on a paired icon/text
@@ -319,18 +412,33 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || bucket.layout.get<style::IconOptional>()),
iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || bucket.layout.get<style::TextOptional>()),
true);
+ DynamicTextOffsets duplicateTextOffset(Point<float>(-INFINITY, -INFINITY), Point<float>(-INFINITY, -INFINITY), Point<float>(-INFINITY, -INFINITY));
for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
bool isDuplicate = seenCrossTileIDs.count(symbolInstance.crossTileID) > 0;
auto it = opacities.find(symbolInstance.crossTileID);
+ auto offsetIt = dynamicOffsets.find(symbolInstance.crossTileID);
auto opacityState = defaultOpacityState;
+ auto dynamicOffsetState = duplicateTextOffset;
if (isDuplicate) {
opacityState = duplicateOpacityState;
} else if (it != opacities.end()) {
opacityState = it->second;
}
+ if (!isDuplicate && dynamicText && offsetIt != dynamicOffsets.end()){
+ dynamicOffsetState = offsetIt->second;
+ }
+
+ if (dynamicText && offsetIt == dynamicOffsets.end()) {
+ auto rightPlaced = bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex);
+ auto centerPlaced = bucket.text.placedSymbols.at(*symbolInstance.placedCenterTextIndex);
+ auto leftPlaced = bucket.text.placedSymbols.at(*symbolInstance.placedLeftTextIndex);
+ dynamicOffsetState = DynamicTextOffsets(rightPlaced.dynamicShift, centerPlaced.dynamicShift, leftPlaced.dynamicShift);
+ dynamicOffsets.emplace(symbolInstance.crossTileID, dynamicOffsetState);
+ }
+
if (it == opacities.end()) {
opacities.emplace(symbolInstance.crossTileID, defaultOpacityState);
}
@@ -339,14 +447,36 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
if (symbolInstance.hasText) {
auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.text.placed, opacityState.text.opacity);
- for (size_t i = 0; i < symbolInstance.horizontalGlyphQuads.size() * 4; i++) {
+ for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuads.size() * 4; i++) {
bucket.text.opacityVertices.emplace_back(opacityVertex);
}
+ if (symbolInstance.placedCenterTextIndex) {
+ for (size_t i = 0; i < symbolInstance.centerJustifiedGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ }
+ if (symbolInstance.placedLeftTextIndex) {
+ for (size_t i = 0; i < symbolInstance.leftJustifiedGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ }
for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
bucket.text.opacityVertices.emplace_back(opacityVertex);
}
- if (symbolInstance.placedTextIndex) {
- bucket.text.placedSymbols[*symbolInstance.placedTextIndex].hidden = opacityState.isHidden();
+ if (symbolInstance.placedRightTextIndex) {
+ PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedRightTextIndex];
+ placed.hidden = opacityState.isHidden();
+ placed.dynamicShift = dynamicOffsetState.right;
+ }
+ if (symbolInstance.placedCenterTextIndex) {
+ PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedCenterTextIndex];
+ placed.hidden = opacityState.isHidden();
+ placed.dynamicShift = dynamicOffsetState.center;
+ }
+ if (symbolInstance.placedLeftTextIndex) {
+ PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedLeftTextIndex];
+ placed.hidden = opacityState.isHidden();
+ placed.dynamicShift = dynamicOffsetState.left;
}
if (symbolInstance.placedVerticalTextIndex) {
bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden();
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
index cc23110e54..ae8e432b8e 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -4,8 +4,10 @@
#include <unordered_map>
#include <mbgl/util/chrono.hpp>
#include <mbgl/text/collision_index.hpp>
+#include <mbgl/text/shaping.hpp>
#include <mbgl/layout/symbol_projection.hpp>
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/util/geometry.hpp>
#include <unordered_set>
namespace mbgl {
@@ -31,6 +33,14 @@ public:
OpacityState text;
};
+class DynamicTextOffsets {
+public:
+ DynamicTextOffsets(Point<float> right, Point<float> center, Point<float> left);
+ Point<float> right;
+ Point<float> center;
+ Point<float> left;
+};
+
class JointPlacement {
public:
JointPlacement(bool text_, bool icon_, bool skipFade_)
@@ -121,6 +131,7 @@ private:
std::unordered_map<uint32_t, JointPlacement> placements;
std::unordered_map<uint32_t, JointOpacityState> opacities;
+ std::unordered_map<uint32_t, DynamicTextOffsets> dynamicOffsets;
bool stale = false;
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index 9d582f14d6..4d33ef1a0c 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -97,10 +97,9 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const GlyphPositions& positions) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
- const float oneEm = 24.0;
std::array<float, 2> textOffset = layout.get<TextOffset>();
- textOffset[0] *= oneEm;
- textOffset[1] *= oneEm;
+ textOffset[0] *= util::ONE_EM;
+ textOffset[1] *= util::ONE_EM;
SymbolQuads quads;
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 3a6335955b..eb7a0a848d 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -10,58 +10,8 @@
namespace mbgl {
-struct AnchorAlignment {
- AnchorAlignment(float horizontal_, float vertical_)
- : horizontalAlign(horizontal_), verticalAlign(vertical_) {
- }
-
- float horizontalAlign;
- float verticalAlign;
-};
-
-AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
-
- switch (anchor) {
- case style::SymbolAnchorType::Top:
- case style::SymbolAnchorType::Bottom:
- case style::SymbolAnchorType::Center:
- break;
- case style::SymbolAnchorType::Right:
- case style::SymbolAnchorType::TopRight:
- case style::SymbolAnchorType::BottomRight:
- horizontalAlign = 1;
- break;
- case style::SymbolAnchorType::Left:
- case style::SymbolAnchorType::TopLeft:
- case style::SymbolAnchorType::BottomLeft:
- horizontalAlign = 0;
- break;
- }
-
- switch (anchor) {
- case style::SymbolAnchorType::Left:
- case style::SymbolAnchorType::Right:
- case style::SymbolAnchorType::Center:
- break;
- case style::SymbolAnchorType::Bottom:
- case style::SymbolAnchorType::BottomLeft:
- case style::SymbolAnchorType::BottomRight:
- verticalAlign = 1;
- break;
- case style::SymbolAnchorType::Top:
- case style::SymbolAnchorType::TopLeft:
- case style::SymbolAnchorType::TopRight:
- verticalAlign = 0;
- break;
- }
-
- return AnchorAlignment(horizontalAlign, verticalAlign);
-}
-
PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
- AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor);
+ AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
@@ -339,7 +289,7 @@ void shapeLines(Shaping& shaping,
y += lineHeight * lineMaxScale;
}
- auto anchorAlign = getAnchorAlignment(textAnchor);
+ auto anchorAlign = AnchorAlignment::getAnchorAlignment(textAnchor);
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
lineHeight, lines.size());
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 50ac893098..3a688b1756 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -7,6 +7,56 @@
namespace mbgl {
+struct AnchorAlignment {
+ AnchorAlignment(float horizontal_, float vertical_)
+ : horizontalAlign(horizontal_), verticalAlign(vertical_) {
+ }
+
+ static AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::TopRight:
+ case style::SymbolAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::BottomLeft:
+ case style::SymbolAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ return AnchorAlignment(horizontalAlign, verticalAlign);
+ }
+
+ float horizontalAlign;
+ float verticalAlign;
+};
+
class SymbolFeature;
class BiDi;