diff options
Diffstat (limited to 'src/mbgl/text')
-rw-r--r-- | src/mbgl/text/placement.cpp | 139 | ||||
-rw-r--r-- | src/mbgl/text/quads.cpp | 48 | ||||
-rw-r--r-- | src/mbgl/text/quads.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/text/shaping.cpp | 45 | ||||
-rw-r--r-- | src/mbgl/text/shaping.hpp | 7 |
5 files changed, 162 insertions, 81 deletions
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 27c4913c63..1428cf7792 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -157,6 +157,7 @@ void Placement::placeBucket( std::vector<style::TextVariableAnchorType> variableTextAnchors = layout.get<style::TextVariableAnchor>(); const bool rotateWithMap = layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map; const bool pitchWithMap = layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map; + const bool hasIconTextFit = layout.get<style::IconTextFit>() != style::IconTextFitType::None; const bool hasCollisionCircleData = bucket.hasCollisionCircleData(); const bool zOrderByViewportY = layout.get<style::SymbolZOrder>() == style::SymbolZOrderType::ViewportY; @@ -179,7 +180,9 @@ void Placement::placeBucket( bool placeIcon = false; bool offscreen = true; std::pair<bool, bool> placed{ false, false }; - std::pair<bool, bool> placedVertical{ false, false }; + std::pair<bool, bool> placedVerticalText{ false, false }; + std::pair<bool, bool> placedVerticalIcon{ false, false }; + Point<float> shift{0.0f, 0.0f}; optional<size_t> horizontalTextIndex = symbolInstance.getDefaultHorizontalPlacedTextIndex(); if (horizontalTextIndex) { const PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex); @@ -200,7 +203,7 @@ void Placement::placeBucket( assert(!bucket.placementModes.empty()); for (auto& placementMode : bucket.placementModes) { if (placementMode == style::TextWritingModeType::Vertical) { - placedVertical = placed = placeVerticalFn(); + placedVerticalText = placed = placeVerticalFn(); } else { placed = placeHorizontalFn(); } @@ -280,7 +283,7 @@ void Placement::placeBucket( for (size_t i = 0u; i < placementAttempts; ++i) { auto anchor = variableTextAnchors[i % anchorsSize]; const bool allowOverlap = (i >= anchorsSize); - Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale); + shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale); if (rotateWithMap) { float angle = pitchWithMap ? state.getBearing() : -state.getBearing(); shift = util::rotate(shift, angle); @@ -359,15 +362,28 @@ void Placement::placeBucket( } if (symbolInstance.placedIconIndex) { + if (!hasIconTextFit || !placeText || variableTextAnchors.empty()) { + shift = {0.0f, 0.0f}; + } + const PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex); const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol); + const auto& placeIconFeature = [&] (const CollisionFeature& collisionFeature) { + return collisionIndex.placeFeature(collisionFeature, shift, + posMatrix, iconLabelPlaneMatrix, pixelRatio, + placedSymbol, scale, fontSize, + layout.get<style::IconAllowOverlap>(), + pitchWithMap, + params.showCollisionBoxes, avoidEdges, + collisionGroup.second, iconBoxes); + }; - auto placedIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {}, - posMatrix, iconLabelPlaneMatrix, pixelRatio, - placedSymbol, scale, fontSize, - layout.get<style::IconAllowOverlap>(), - pitchWithMap, - params.showCollisionBoxes, avoidEdges, collisionGroup.second, iconBoxes); + std::pair<bool, bool> placedIcon = {false, false}; + if (placedVerticalText.first && symbolInstance.verticalIconCollisionFeature) { + placedIcon = placedVerticalIcon = placeIconFeature(*symbolInstance.verticalIconCollisionFeature); + } else { + placedIcon = placeIconFeature(symbolInstance.iconCollisionFeature); + } placeIcon = placedIcon.first; offscreen &= placedIcon.second; } @@ -385,7 +401,7 @@ void Placement::placeBucket( } if (placeText) { - if (placedVertical.first && symbolInstance.verticalTextCollisionFeature) { + if (placedVerticalText.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); @@ -393,7 +409,11 @@ void Placement::placeBucket( } if (placeIcon) { - collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first); + if (placedVerticalIcon.first && symbolInstance.verticalIconCollisionFeature) { + collisionIndex.insertFeature(*symbolInstance.verticalIconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first); + } else { + collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first); + } } if (hasCollisionCircleData) { @@ -518,6 +538,8 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor using namespace style; const auto& layout = *bucket.layout; const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point; + const bool hasVariableAnchors = !layout.get<TextVariableAnchor>().empty() && bucket.hasTextData(); + const bool updateTextFitIcon = layout.get<IconTextFit>() != IconTextFitType::None && (bucket.allowVerticalPlacement || hasVariableAnchors) && bucket.hasIconData(); bool result = false; if (alongLine) { @@ -538,7 +560,7 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor tile, *bucket.textSizeBinder, state); result = true; } - } else if (!layout.get<TextVariableAnchor>().empty() && bucket.hasTextData()) { + } else if (hasVariableAnchors) { bucket.text.dynamicVertices.clear(); bucket.hasVariablePlacement = false; @@ -548,8 +570,10 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map; const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom()); const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); + std::unordered_map<std::size_t, std::pair<std::size_t, Point<float>>> placedTextShifts; - for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { + for (std::size_t i = 0; i < bucket.text.placedSymbols.size(); ++i) { + const PlacedSymbol& symbol = bucket.text.placedSymbols[i]; optional<VariableOffset> variableOffset; const bool skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation; if (!symbol.hidden && symbol.crossTileID != 0u && !skipOrientation) { @@ -598,24 +622,57 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor projectedAnchor.first.y + shift.y); } - for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) { + if (updateTextFitIcon && symbol.placedIconIndex) { + placedTextShifts.emplace(*symbol.placedIconIndex, + std::pair<std::size_t, Point<float>>{i, shiftedAnchor}); + } + + for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) { addDynamicAttributes(shiftedAnchor, symbol.angle, bucket.text.dynamicVertices); } } } + if (updateTextFitIcon && bucket.hasVariablePlacement) { + bucket.icon.dynamicVertices.clear(); + for (std::size_t i = 0; i < bucket.icon.placedSymbols.size(); ++i) { + const PlacedSymbol& placedIcon = bucket.icon.placedSymbols[i]; + if (placedIcon.hidden || (!placedIcon.placedOrientation && bucket.allowVerticalPlacement)) { + hideGlyphs(placedIcon.glyphOffsets.size(), bucket.icon.dynamicVertices); + } else { + const auto& pair = placedTextShifts.find(i); + if (pair == placedTextShifts.end()) { + hideGlyphs(placedIcon.glyphOffsets.size(), bucket.icon.dynamicVertices); + } else { + for (std::size_t j = 0; j < placedIcon.glyphOffsets.size(); ++j) { + addDynamicAttributes(pair->second.second, placedIcon.angle, bucket.icon.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); + const auto updateDynamicVertices = [](SymbolBucket::Buffer& buffer) { + buffer.dynamicVertices.clear(); + for (const PlacedSymbol& symbol : buffer.placedSymbols) { + if (symbol.hidden || !symbol.placedOrientation) { + hideGlyphs(symbol.glyphOffsets.size(), buffer.dynamicVertices); + } else { + for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) { + addDynamicAttributes(symbol.anchorPoint, symbol.angle, buffer.dynamicVertices); + } } } + }; + + updateDynamicVertices(bucket.text); + // When text box is rotated, icon-text-fit icon must be rotated as well. + if (updateTextFitIcon) { + updateDynamicVertices(bucket.icon); } + result = true; } @@ -635,6 +692,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState const bool variablePlacement = !bucket.layout->get<style::TextVariableAnchor>().empty(); const bool rotateWithMap = bucket.layout->get<style::TextRotationAlignment>() == style::AlignmentType::Map; const bool pitchWithMap = bucket.layout->get<style::TextPitchAlignment>() == style::AlignmentType::Map; + const bool hasIconTextFit = bucket.layout->get<style::IconTextFit>() != style::IconTextFitType::None; // 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 @@ -703,25 +761,30 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState } if (symbolInstance.hasIcon) { const auto& opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity); - bucket.icon.opacityVertices.extend(4, opacityVertex); if (symbolInstance.placedIconIndex) { + bucket.icon.opacityVertices.extend(4, opacityVertex); bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden(); } + + if (symbolInstance.placedVerticalIconIndex) { + bucket.icon.opacityVertices.extend(4, opacityVertex); + bucket.icon.placedSymbols[*symbolInstance.placedVerticalIconIndex].hidden = opacityState.isHidden(); + } } - auto updateCollisionBox = [&](const auto& feature, const bool placed) { + auto updateCollisionBox = [&](const auto& feature, const bool placed, const Point<float>& shift) { if (feature.alongLine) { return; } - const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, {}); + const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, shift); bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex); }; auto updateCollisionTextBox = [this, &bucket, &symbolInstance, &state, variablePlacement, rotateWithMap, pitchWithMap](const auto& feature, const bool placed) { + Point<float> shift{0.0f, 0.0f}; if (feature.alongLine) { - return; + return shift; } - Point<float> shift; bool used = true; if (variablePlacement) { auto foundOffset = variableOffsets.find(symbolInstance.crossTileID); @@ -748,6 +811,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState } const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift); bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex); + return shift; }; auto updateCollisionCircles = [&](const auto& feature, const bool placed) { @@ -768,12 +832,17 @@ 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); + const auto& textShift = updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed); + if (bucket.allowVerticalPlacement) { + Point<float> verticalTextShift{0.0f, 0.0f}; + if (symbolInstance.verticalTextCollisionFeature) { + verticalTextShift = updateCollisionTextBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed); + } + if (symbolInstance.verticalIconCollisionFeature) { + updateCollisionBox(*symbolInstance.verticalIconCollisionFeature, opacityState.text.placed, hasIconTextFit ? verticalTextShift : Point<float>{0.0f, 0.0f}); + } } - updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed); + updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed, hasIconTextFit ? textShift : Point<float>{0.0f, 0.0f}); } if (bucket.hasCollisionCircleData()) { updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed); @@ -850,6 +919,14 @@ void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingMode if (symbolInstance.placedVerticalTextIndex) { bucket.text.placedSymbols.at(*symbolInstance.placedVerticalTextIndex).placedOrientation = vertical; } + + if (symbolInstance.placedIconIndex) { + bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex).placedOrientation = horizontal; + } + + if (symbolInstance.placedVerticalIconIndex) { + bucket.icon.placedSymbols.at(*symbolInstance.placedVerticalIconIndex).placedOrientation = vertical; + } } float Placement::symbolFadeChange(TimePoint now) const { diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index b08c2bc0ba..281c5d99de 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -14,9 +14,7 @@ namespace mbgl { using namespace style; SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, - const SymbolLayoutProperties::Evaluated& layout, - const float layoutTextSize, - const Shaping& shapedText) { + WritingModeType writingMode) { const ImagePosition& image = shapedIcon.image(); // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual @@ -28,43 +26,11 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, float left = shapedIcon.left() - border / image.pixelRatio; float bottom = shapedIcon.bottom() + border / image.pixelRatio; float right = shapedIcon.right() + border / image.pixelRatio; - Point<float> tl; - Point<float> tr; - Point<float> br; - Point<float> bl; - - if (layout.get<IconTextFit>() != IconTextFitType::None && shapedText) { - auto iconWidth = right - left; - auto iconHeight = bottom - top; - auto size = layoutTextSize / 24.0f; - auto textLeft = shapedText.left * size; - auto textRight = shapedText.right * size; - auto textTop = shapedText.top * size; - auto textBottom = shapedText.bottom * size; - auto textWidth = textRight - textLeft; - auto textHeight = textBottom - textTop; - auto padT = layout.get<IconTextFitPadding>()[0]; - auto padR = layout.get<IconTextFitPadding>()[1]; - auto padB = layout.get<IconTextFitPadding>()[2]; - auto padL = layout.get<IconTextFitPadding>()[3]; - auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; - auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; - auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth; - auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight; - left = textLeft + offsetX - padL; - top = textTop + offsetY - padT; - right = textLeft + offsetX + padR + width; - bottom = textTop + offsetY + padB + height; - tl = {left, top}; - tr = {right, top}; - br = {right, bottom}; - bl = {left, bottom}; - } else { - tl = {left, top}; - tr = {right, top}; - br = {right, bottom}; - bl = {left, bottom}; - } + + Point<float> tl{left, top}; + Point<float> tr{right, top}; + Point<float> br{right, bottom}; + Point<float> bl{left, bottom}; const float angle = shapedIcon.angle(); @@ -88,7 +54,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, static_cast<uint16_t>(image.textureRect.h + border * 2) }; - return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } }; + return SymbolQuad { tl, tr, bl, br, textureRect, writingMode, { 0.0f, 0.0f } }; } SymbolQuads getGlyphQuads(const Shaping& shapedText, diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 1ec68189af..145fd2b153 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -44,9 +44,7 @@ public: using SymbolQuads = std::vector<SymbolQuad>; SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, - const style::SymbolLayoutProperties::Evaluated&, - const float layoutTextSize, - const Shaping& shapedText); + WritingModeType writingMode); SymbolQuads getGlyphQuads(const Shaping& shapedText, const std::array<float, 2> textOffset, diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 7bf0e14f80..0cb9ea73a9 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -68,16 +68,49 @@ style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor) { } } -PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) { +PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, + const std::array<float, 2>& iconOffset, + style::SymbolAnchorType iconAnchor, + const float iconRotation) { AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment(iconAnchor); float dx = iconOffset[0]; float dy = iconOffset[1]; - float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign; - float x2 = x1 + image.displaySize()[0]; - float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign; - float y2 = y1 + image.displaySize()[1]; + float left = dx - image.displaySize()[0] * anchorAlign.horizontalAlign; + float right = left + image.displaySize()[0]; + float top = dy - image.displaySize()[1] * anchorAlign.verticalAlign; + float bottom = top + image.displaySize()[1]; - return PositionedIcon { image, y1, y2, x1, x2, iconRotation }; + return PositionedIcon { image, top, bottom, left, right, iconRotation }; +} + +void PositionedIcon::fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout, + const Shaping& shapedText, + float layoutTextSize) { + using namespace style; + assert(layout.get<IconTextFit>() != IconTextFitType::None); + if (shapedText) { + auto iconWidth = _right - _left; + auto iconHeight = _bottom - _top; + auto size = layoutTextSize / 24.0f; + auto textLeft = shapedText.left * size; + auto textRight = shapedText.right * size; + auto textTop = shapedText.top * size; + auto textBottom = shapedText.bottom * size; + auto textWidth = textRight - textLeft; + auto textHeight = textBottom - textTop; + auto padT = layout.get<IconTextFitPadding>()[0]; + auto padR = layout.get<IconTextFitPadding>()[1]; + auto padB = layout.get<IconTextFitPadding>()[2]; + auto padL = layout.get<IconTextFitPadding>()[3]; + auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; + auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; + auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth; + auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight; + _left = textLeft + offsetX - padL; + _top = textTop + offsetY - padT; + _right = textLeft + offsetX + padR + width; + _bottom = textTop + offsetY + padB + height; + } } void align(Shaping& shaping, diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index f3a01e3caf..60a9c718ff 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -4,6 +4,7 @@ #include <mbgl/text/tagged_string.hpp> #include <mbgl/renderer/image_atlas.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/layers/symbol_layer_properties.hpp> namespace mbgl { @@ -52,6 +53,12 @@ public: style::SymbolAnchorType iconAnchor, const float iconRotation); + // Updates shaped icon's bounds based on shaped text's bounds and provided + // layout properties. + void fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout, + const Shaping& shapedText, + float layoutTextSize); + const ImagePosition& image() const { return _image; } float top() const { return _top; } float bottom() const { return _bottom; } |