diff options
author | Molly Lloyd <molly@mapbox.com> | 2018-12-18 15:52:38 -0800 |
---|---|---|
committer | Molly Lloyd <molly@mapbox.com> | 2018-12-18 15:52:38 -0800 |
commit | e6bdd4235589dfbaf692d2fbfd8706736aa31060 (patch) | |
tree | 2689c3fab87ec9d3e7dd973fc247d6f3fef2cc2f /src/mbgl/text | |
parent | 7db9ce37e648e8145816f11dcdb3251f0ce81d74 (diff) | |
download | qtlocation-mapboxgl-e6bdd4235589dfbaf692d2fbfd8706736aa31060.tar.gz |
implement dynamic-text-anchor property
Diffstat (limited to 'src/mbgl/text')
-rw-r--r-- | src/mbgl/text/collision_index.cpp | 10 | ||||
-rw-r--r-- | src/mbgl/text/collision_index.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/placement.cpp | 146 | ||||
-rw-r--r-- | src/mbgl/text/placement.hpp | 11 | ||||
-rw-r--r-- | src/mbgl/text/quads.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/text/shaping.cpp | 54 | ||||
-rw-r--r-- | src/mbgl/text/shaping.hpp | 50 |
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; |