summaryrefslogtreecommitdiff
path: root/src/mbgl/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text')
-rw-r--r--src/mbgl/text/glyph.hpp2
-rw-r--r--src/mbgl/text/placement.cpp277
-rw-r--r--src/mbgl/text/placement.hpp5
-rw-r--r--src/mbgl/text/quads.cpp34
-rw-r--r--src/mbgl/text/quads.hpp3
-rw-r--r--src/mbgl/text/shaping.cpp10
-rw-r--r--src/mbgl/text/tagged_string.cpp8
-rw-r--r--src/mbgl/text/tagged_string.hpp2
8 files changed, 254 insertions, 87 deletions
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 5105528512..234f718975 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -89,6 +89,8 @@ class Shaping {
WritingModeType writingMode;
std::size_t lineCount = 0u;
explicit operator bool() const { return !positionedGlyphs.empty(); }
+ // The y offset *should* be part of the font metadata.
+ static constexpr int32_t yOffset = -17;
};
enum class WritingModeType : uint8_t {
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index f7d13dcb26..de282acf79 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -172,32 +172,80 @@ void Placement::placeBucket(
placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
return;
}
- textBoxes.clear();
iconBoxes.clear();
bool placeText = false;
bool placeIcon = false;
bool offscreen = true;
+ std::pair<bool, bool> placed{ false, false };
+ std::pair<bool, bool> placedVertical{ false, false };
optional<size_t> horizontalTextIndex = symbolInstance.getDefaultHorizontalPlacedTextIndex();
if (horizontalTextIndex) {
- const CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature;
const PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+ const CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature;
+
+ const auto updatePreviousOrientationIfNotPlaced = [&](bool isPlaced) {
+ if (bucket.allowVerticalPlacement && !isPlaced && prevPlacement) {
+ auto prevOrientation = prevPlacement->placedOrientations.find(symbolInstance.crossTileID);
+ if (prevOrientation != prevPlacement->placedOrientations.end()) {
+ placedOrientations[symbolInstance.crossTileID] = prevOrientation->second;
+ }
+ }
+ };
+
+ const auto placeTextForPlacementModes = [&] (auto& placeHorizontalFn, auto& placeVerticalFn) {
+ if (bucket.allowVerticalPlacement && symbolInstance.writingModes & WritingModeType::Vertical) {
+ assert(!bucket.placementModes.empty());
+ for (auto& placementMode : bucket.placementModes) {
+ if (placementMode == style::TextWritingModeType::Vertical) {
+ placedVertical = placed = placeVerticalFn();
+ } else {
+ placed = placeHorizontalFn();
+ }
+
+ if (placed.first) {
+ break;
+ }
+ }
+ } else {
+ placed = placeHorizontalFn();
+ }
+ };
+
+ // Line or point label placement
if (variableTextAnchors.empty()) {
- auto placed = collisionIndex.placeFeature(textCollisionFeature, {},
- posMatrix, textLabelPlaneMatrix, pixelRatio,
- placedSymbol, scale, fontSize,
- layout.get<style::TextAllowOverlap>(),
- pitchWithMap,
- params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
+ const auto placeFeature = [&] (const CollisionFeature& collisionFeature, style::TextWritingModeType orientation) {
+ textBoxes.clear();
+ auto placedFeature = collisionIndex.placeFeature(collisionFeature, {},
+ posMatrix, textLabelPlaneMatrix, pixelRatio,
+ placedSymbol, scale, fontSize,
+ layout.get<style::TextAllowOverlap>(),
+ pitchWithMap,
+ params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
+ if (placedFeature.first) {
+ placedOrientations.emplace(symbolInstance.crossTileID, orientation);
+ }
+ return placedFeature;
+ };
+
+ const auto placeHorizontal = [&] {
+ return placeFeature(symbolInstance.textCollisionFeature, style::TextWritingModeType::Horizontal);
+ };
+
+ const auto placeVertical = [&] {
+ if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
+ return placeFeature(*symbolInstance.verticalTextCollisionFeature, style::TextWritingModeType::Vertical);
+ }
+ return std::pair<bool, bool>{false, false};
+ };
+
+ placeTextForPlacementModes(placeHorizontal, placeVertical);
+ updatePreviousOrientationIfNotPlaced(placed.first);
+
placeText = placed.first;
offscreen &= placed.second;
} else if (!textCollisionFeature.alongLine && !textCollisionFeature.boxes.empty()) {
- const CollisionBox& textBox = symbolInstance.textCollisionFeature.boxes[0];
- const float width = textBox.x2 - textBox.x1;
- const float height = textBox.y2 - textBox.y1;
- const float textBoxScale = symbolInstance.textBoxScale;
-
// If this symbol was in the last placement, shift the previously used
// anchor to the front of the anchor list, only if the previous anchor
// is still in the anchor list.
@@ -220,55 +268,83 @@ void Placement::placeBucket(
}
}
- for (auto anchor : variableTextAnchors) {
- Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
- if (rotateWithMap) {
- float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
- shift = util::rotate(shift, angle);
- }
+ const auto placeFeatureForVariableAnchors = [&] (const CollisionFeature& collisionFeature, style::TextWritingModeType orientation) {
+ const CollisionBox& textBox = collisionFeature.boxes[0];
+ const float width = textBox.x2 - textBox.x1;
+ const float height = textBox.y2 - textBox.y1;
+ const float textBoxScale = symbolInstance.textBoxScale;
+ std::pair<bool, bool> placedFeature = {false, false};
+ for (auto anchor : variableTextAnchors) {
+ Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
+ if (rotateWithMap) {
+ float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
+ shift = util::rotate(shift, angle);
+ }
+
+ textBoxes.clear();
+ placedFeature = collisionIndex.placeFeature(collisionFeature, shift,
+ posMatrix, mat4(), pixelRatio,
+ placedSymbol, scale, fontSize,
+ layout.get<style::TextAllowOverlap>(),
+ pitchWithMap,
+ params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
+ if (placedFeature.first) {
+ assert(symbolInstance.crossTileID != 0u);
+ optional<style::TextVariableAnchorType> prevAnchor;
+
+ // If this label was placed in the previous placement, record the anchor position
+ // to allow us to animate the transition
+ if (prevPlacement) {
+ auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
+ auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID);
+ if (prevOffset != prevPlacement->variableOffsets.end() &&
+ prevPlacements != prevPlacement->placements.end() &&
+ prevPlacements->second.text) {
+ // TODO: The prevAnchor seems to be unused, needs to be fixed.
+ prevAnchor = prevOffset->second.anchor;
+ }
+ }
- auto placed = collisionIndex.placeFeature(textCollisionFeature, shift,
- posMatrix, mat4(), pixelRatio,
- placedSymbol, scale, fontSize,
- layout.get<style::TextAllowOverlap>(),
- pitchWithMap,
- params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
-
- if (placed.first) {
- assert(symbolInstance.crossTileID != 0u);
- optional<style::TextVariableAnchorType> prevAnchor;
-
- // If this label was placed in the previous placement, record the anchor position
- // to allow us to animate the transition
- if (prevPlacement) {
- auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
- auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID);
- if (prevOffset != prevPlacement->variableOffsets.end() &&
- prevPlacements != prevPlacement->placements.end() &&
- prevPlacements->second.text) {
- prevAnchor = prevOffset->second.anchor;
+ variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
+ symbolInstance.radialTextOffset,
+ width,
+ height,
+ anchor,
+ textBoxScale,
+ prevAnchor
+ }));
+
+ if (bucket.allowVerticalPlacement) {
+ placedOrientations.emplace(symbolInstance.crossTileID, orientation);
}
+ break;
}
+ }
+
+ return placedFeature;
+ };
- variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
- symbolInstance.radialTextOffset,
- width,
- height,
- anchor,
- textBoxScale,
- prevAnchor
- }));
-
- placeText = placed.first;
- offscreen &= placed.second;
- break;
+ const auto placeHorizontal = [&] {
+ return placeFeatureForVariableAnchors(symbolInstance.textCollisionFeature, style::TextWritingModeType::Horizontal);
+ };
+
+ const auto placeVertical = [&] {
+ if (bucket.allowVerticalPlacement && !placed.first && symbolInstance.verticalTextCollisionFeature) {
+ return placeFeatureForVariableAnchors(*symbolInstance.verticalTextCollisionFeature, style::TextWritingModeType::Vertical);
}
- textBoxes.clear();
- }
+ return std::pair<bool, bool>{false, false};
+ };
+
+ placeTextForPlacementModes(placeHorizontal, placeVertical);
+
+ placeText = placed.first;
+ offscreen &= placed.second;
+
+ updatePreviousOrientationIfNotPlaced(placed.first);
// If we didn't get placed, we still need to copy our position from the last placement for
// fade animations
- if (prevPlacement && variableOffsets.find(symbolInstance.crossTileID) == variableOffsets.end()) {
+ if (!placeText && prevPlacement) {
auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
if (prevOffset != prevPlacement->variableOffsets.end()) {
variableOffsets[symbolInstance.crossTileID] = prevOffset->second;
@@ -281,14 +357,14 @@ void Placement::placeBucket(
const PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
- auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
+ auto placedIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
posMatrix, iconLabelPlaneMatrix, pixelRatio,
placedSymbol, scale, fontSize,
layout.get<style::IconAllowOverlap>(),
pitchWithMap,
params.showCollisionBoxes, avoidEdges, collisionGroup.second, iconBoxes);
- placeIcon = placed.first;
- offscreen &= placed.second;
+ placeIcon = placedIcon.first;
+ offscreen &= placedIcon.second;
}
const bool iconWithoutText = !symbolInstance.hasText || layout.get<style::TextOptional>();
@@ -304,7 +380,11 @@ void Placement::placeBucket(
}
if (placeText) {
- collisionIndex.insertFeature(symbolInstance.textCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ if (placedVertical.first && symbolInstance.verticalTextCollisionFeature) {
+ collisionIndex.insertFeature(*symbolInstance.verticalTextCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ } else {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ }
}
if (placeIcon) {
@@ -399,6 +479,15 @@ void Placement::commit(TimePoint now) {
}
}
+ for (auto& prevOrientation : prevPlacement->placedOrientations) {
+ const uint32_t crossTileID = prevOrientation.first;
+ auto foundOrientation = placedOrientations.find(crossTileID);
+ auto foundOpacity = opacities.find(crossTileID);
+ if (foundOrientation == placedOrientations.end() && foundOpacity != opacities.end() && !foundOpacity->second.isHidden()) {
+ placedOrientations[prevOrientation.first] = prevOrientation.second;
+ }
+ }
+
fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime;
}
@@ -457,7 +546,8 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
optional<VariableOffset> variableOffset;
- if (!symbol.hidden && symbol.crossTileID != 0u) {
+ const bool skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
+ if (!symbol.hidden && symbol.crossTileID != 0u && !skipOrientation) {
auto it = variableOffsets.find(symbol.crossTileID);
if (it != variableOffsets.end()) {
bucket.hasVariablePlacement = true;
@@ -506,12 +596,24 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
}
for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
- addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices);
+ addDynamicAttributes(shiftedAnchor, symbol.angle, bucket.text.dynamicVertices);
}
}
}
result = true;
+ } else if (bucket.allowVerticalPlacement && bucket.hasTextData()) {
+ bucket.text.dynamicVertices.clear();
+ for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
+ if (symbol.hidden || !symbol.placedOrientation) {
+ hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
+ } else {
+ for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
+ addDynamicAttributes(symbol.anchorPoint, symbol.angle, bucket.text.dynamicVertices);
+ }
+ }
+ }
+ result = true;
}
return result;
@@ -582,9 +684,18 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
bucket.text.opacityVertices.extend(textOpacityVerticesSize, opacityVertex);
- auto offset = variableOffsets.find(symbolInstance.crossTileID);
- if (offset != variableOffsets.end()) {
- markUsedJustification(bucket, offset->second.anchor, symbolInstance);
+ style::TextWritingModeType previousOrientation = style::TextWritingModeType::Horizontal;
+ if (bucket.allowVerticalPlacement) {
+ auto prevOrientation = placedOrientations.find(symbolInstance.crossTileID);
+ if (prevOrientation != placedOrientations.end()) {
+ previousOrientation = prevOrientation->second;
+ markUsedOrientation(bucket, prevOrientation->second, symbolInstance);
+ }
+ }
+
+ auto prevOffset = variableOffsets.find(symbolInstance.crossTileID);
+ if (prevOffset != variableOffsets.end()) {
+ markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance, previousOrientation);
}
}
if (symbolInstance.hasIcon) {
@@ -654,7 +765,11 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
};
if (bucket.hasCollisionBoxData()) {
+ // TODO: update collision box opacity based on selected text variant (horizontal | vertical).
updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
+ if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
+ updateCollisionTextBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
+ }
updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
}
if (bucket.hasCollisionCircleData()) {
@@ -671,7 +786,12 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
}
namespace {
-optional<size_t> justificationToIndex(style::TextJustifyType justify, const SymbolInstance& symbolInstance) {
+optional<size_t> justificationToIndex(style::TextJustifyType justify, const SymbolInstance& symbolInstance, style::TextWritingModeType orientation) {
+ // Vertical symbol has just one justification, style::TextJustifyType::Left.
+ if (orientation == style::TextWritingModeType::Vertical) {
+ return symbolInstance.placedVerticalTextIndex;
+ }
+
switch(justify) {
case style::TextJustifyType::Right: return symbolInstance.placedRightTextIndex;
case style::TextJustifyType::Center: return symbolInstance.placedCenterTextIndex;
@@ -686,13 +806,13 @@ const style::TextJustifyType justifyTypes[] = {style::TextJustifyType::Right, st
} // namespace
-void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableAnchorType placedAnchor, SymbolInstance& symbolInstance) {
+void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableAnchorType placedAnchor, SymbolInstance& symbolInstance, style::TextWritingModeType orientation) {
style::TextJustifyType anchorJustify = getAnchorJustification(placedAnchor);
assert(anchorJustify != style::TextJustifyType::Auto);
- const optional<size_t>& autoIndex = justificationToIndex(anchorJustify, symbolInstance);
+ const optional<size_t>& autoIndex = justificationToIndex(anchorJustify, symbolInstance, orientation);
for (auto& justify : justifyTypes) {
- const optional<size_t> index = justificationToIndex(justify, symbolInstance);
+ const optional<size_t> index = justificationToIndex(justify, symbolInstance, orientation);
if (index) {
assert(bucket.text.placedSymbols.size() > *index);
if (autoIndex && *index != *autoIndex) {
@@ -706,6 +826,29 @@ void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableA
}
}
+void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingModeType orientation, SymbolInstance& symbolInstance) {
+ auto horizontal = orientation == style::TextWritingModeType::Horizontal ?
+ optional<style::TextWritingModeType>(orientation) : nullopt;
+ auto vertical = orientation == style::TextWritingModeType::Vertical ?
+ optional<style::TextWritingModeType>(orientation) : nullopt;
+
+ if (symbolInstance.placedRightTextIndex) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedCenterTextIndex && !symbolInstance.singleLine) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedCenterTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedLeftTextIndex && !symbolInstance.singleLine) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedLeftTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedVerticalTextIndex) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedVerticalTextIndex).placedOrientation = vertical;
+ }
+}
+
float Placement::symbolFadeChange(TimePoint now) const {
if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions &&
transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0)) {
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
index 2a6a2e1d6e..33bfbd6527 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -12,6 +12,7 @@ namespace mbgl {
class SymbolBucket;
class SymbolInstance;
+enum class PlacedSymbolOrientation : bool;
class OpacityState {
public:
@@ -122,7 +123,8 @@ private:
// Returns `true` if bucket vertices were updated; returns `false` otherwise.
bool updateBucketDynamicVertices(SymbolBucket&, const TransformState&, const RenderTile& tile) const;
void updateBucketOpacities(SymbolBucket&, const TransformState&, std::set<uint32_t>&);
- void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&);
+ void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&, style::TextWritingModeType orientation);
+ void markUsedOrientation(SymbolBucket&, style::TextWritingModeType, SymbolInstance&);
CollisionIndex collisionIndex;
@@ -135,6 +137,7 @@ private:
std::unordered_map<uint32_t, JointPlacement> placements;
std::unordered_map<uint32_t, JointOpacityState> opacities;
std::unordered_map<uint32_t, VariableOffset> variableOffsets;
+ std::unordered_map<uint32_t, style::TextWritingModeType> placedOrientations;
bool stale = false;
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index 6be5d8c01e..9ff26ddd8d 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -95,8 +95,10 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
- const GlyphPositions& positions) {
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
+ const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point;
SymbolQuads quads;
@@ -117,16 +119,23 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const float rectBuffer = 3.0f + glyphPadding;
const float halfAdvance = glyph.metrics.advance * positionedGlyph.scale / 2.0;
- const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point;
const Point<float> glyphOffset = alongLine ?
Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } :
Point<float>{ 0.0f, 0.0f };
- const Point<float> builtInOffset = alongLine ?
+ Point<float> builtInOffset = alongLine ?
Point<float>{ 0.0f, 0.0f } :
Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] };
+ Point<float> verticalizedLabelOffset = { 0.0f, 0.0f };
+ const bool rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical;
+ if (rotateVerticalGlyph) {
+ // Vertical POI labels, that are rotated 90deg CW and whose glyphs must preserve upright orientation
+ // need to be rotated 90deg CCW. After quad is rotated, it is translated to the original built-in offset.
+ verticalizedLabelOffset = builtInOffset;
+ builtInOffset = { 0.0f, 0.0f };
+ }
const float x1 = (glyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset.x;
const float y1 = (-glyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y;
@@ -138,22 +147,25 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
Point<float> bl{x1, y2};
Point<float> br{x2, y2};
- if (alongLine && positionedGlyph.vertical) {
+ if (rotateVerticalGlyph) {
// Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
// In horizontal orientation, the y values for glyphs are below the midline
// and we use a "yOffset" of -17 to pull them up to the middle.
// By rotating counter-clockwise around the point at the center of the left
// edge of a 24x24 layout box centered below the midline, we align the center
// of the glyphs with the horizontal midline, so the yOffset is no longer
- // necessary, but we also pull the glyph to the left along the x axis
- const Point<float> center{-halfAdvance, halfAdvance};
+ // necessary, but we also pull the glyph to the left along the x axis.
+ // The y coordinate includes baseline yOffset, therefore, needs to be accounted
+ // for when glyph is rotated and translated.
+
+ const Point<float> center{ -halfAdvance, halfAdvance - Shaping::yOffset };
const float verticalRotation = -M_PI_2;
- const Point<float> xOffsetCorrection{5, 0};
+ const Point<float> xOffsetCorrection{ 5.0f - Shaping::yOffset, 0.0f };
- tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection;
- tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection;
- bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection;
- br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection;
+ tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
}
if (textRotate) {
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index 0bb892e4d1..1ec68189af 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -52,6 +52,7 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
- const GlyphPositions& positions);
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement);
} // namespace mbgl
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 1d95376b04..132f74661e 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -292,12 +292,8 @@ void shapeLines(Shaping& shaping,
const style::TextJustifyType textJustify,
const WritingModeType writingMode,
const GlyphMap& glyphMap) {
-
- // the y offset *should* be part of the font metadata
- const int32_t yOffset = -17;
-
float x = 0;
- float y = yOffset;
+ float y = Shaping::yOffset;
float maxLineLength = 0;
@@ -342,7 +338,7 @@ void shapeLines(Shaping& shaping,
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale, sectionIndex);
x += glyph.metrics.advance * section.scale + spacing;
} else {
- shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
+ shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
x += util::ONE_EM * section.scale + spacing;
}
}
@@ -364,7 +360,7 @@ void shapeLines(Shaping& shaping,
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
lineHeight, lines.size());
- const float height = y - yOffset;
+ const float height = y - Shaping::yOffset;
// Calculate the bounding box
shaping.top += -anchorAlign.verticalAlign * height;
diff --git a/src/mbgl/text/tagged_string.cpp b/src/mbgl/text/tagged_string.cpp
index 8c4e3b02e8..e8a1c6f51f 100644
--- a/src/mbgl/text/tagged_string.cpp
+++ b/src/mbgl/text/tagged_string.cpp
@@ -8,6 +8,7 @@ void TaggedString::addSection(const std::u16string& sectionText, double scale, F
styledText.first += sectionText;
sections.emplace_back(scale, fontStack, std::move(textColor));
styledText.second.resize(styledText.first.size(), sections.size() - 1);
+ supportsVerticalWritingMode = nullopt;
}
void TaggedString::trim() {
@@ -37,4 +38,11 @@ void TaggedString::verticalizePunctuation() {
styledText.first = util::i18n::verticalizePunctuation(styledText.first);
}
+bool TaggedString::allowsVerticalWritingMode() {
+ if (!supportsVerticalWritingMode) {
+ supportsVerticalWritingMode = util::i18n::allowsVerticalWritingMode(rawText());
+ }
+ return *supportsVerticalWritingMode;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/text/tagged_string.hpp b/src/mbgl/text/tagged_string.hpp
index 2607e10889..698e539a45 100644
--- a/src/mbgl/text/tagged_string.hpp
+++ b/src/mbgl/text/tagged_string.hpp
@@ -98,10 +98,12 @@ struct TaggedString {
void trim();
void verticalizePunctuation();
+ bool allowsVerticalWritingMode();
private:
StyledText styledText;
std::vector<SectionOptions> sections;
+ optional<bool> supportsVerticalWritingMode;
};
} // namespace mbgl