diff options
Diffstat (limited to 'src/mbgl/text')
-rw-r--r-- | src/mbgl/text/glyph.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/placement.cpp | 277 | ||||
-rw-r--r-- | src/mbgl/text/placement.hpp | 5 | ||||
-rw-r--r-- | src/mbgl/text/quads.cpp | 34 | ||||
-rw-r--r-- | src/mbgl/text/quads.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/text/shaping.cpp | 10 | ||||
-rw-r--r-- | src/mbgl/text/tagged_string.cpp | 8 | ||||
-rw-r--r-- | src/mbgl/text/tagged_string.hpp | 2 |
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 |