summaryrefslogtreecommitdiff
path: root/src/mbgl/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text')
-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
7 files changed, 211 insertions, 67 deletions
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;