diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 809aa2f45..133cdec69 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -18,55 +18,22 @@ const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientation } // namespace -SymbolInstance::SymbolInstance(Anchor& anchor_, - GeometryCoordinates line_, - const ShapedTextOrientations& shapedTextOrientations, - optional shapedIcon, - const SymbolLayoutProperties::Evaluated& layout, - const float layoutTextSize, - const float textBoxScale_, - const float textPadding, - const SymbolPlacementType textPlacement, - const std::array textOffset_, - const float iconBoxScale, - const float iconPadding, - const std::array iconOffset_, - const GlyphPositions& positions, - const IndexedSubfeature& indexedFeature, - const std::size_t layoutFeatureIndex_, - const std::size_t dataFeatureIndex_, - std::u16string key_, - const float overscaling, - const float rotate, - float radialTextOffset_) : - anchor(anchor_), - line(line_), - hasText(false), - hasIcon(shapedIcon), - - // Create the collision features that will be used to check whether this symbol instance can be placed - // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature - textCollisionFeature(line_, anchor, getAnyShaping(shapedTextOrientations), textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, rotate), - iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, rotate), - writingModes(WritingModeType::None), - layoutFeatureIndex(layoutFeatureIndex_), - dataFeatureIndex(dataFeatureIndex_), - textOffset(textOffset_), - iconOffset(iconOffset_), - key(std::move(key_)), - textBoxScale(textBoxScale_), - radialTextOffset(radialTextOffset_), - singleLine(shapedTextOrientations.singleLine) { - +SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_, + const ShapedTextOrientations& shapedTextOrientations, + const optional& shapedIcon, + const style::SymbolLayoutProperties::Evaluated& layout, + const float layoutTextSize, + const style::SymbolPlacementType textPlacement, + const std::array& textOffset, + const GlyphPositions& positions) : line(std::move(line_)) { // Create the quads used for rendering the icon and glyphs. if (shapedIcon) { iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.horizontal); } - + bool singleLineInitialized = false; const auto initHorizontalGlyphQuads = [&] (SymbolQuads& quads, const Shaping& shaping) { - writingModes |= WritingModeType::Horizontal; - if (!singleLine) { + if (!shapedTextOrientations.singleLine) { quads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions); return; } @@ -75,7 +42,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, singleLineInitialized = true; } }; - + if (shapedTextOrientations.right) { initHorizontalGlyphQuads(rightJustifiedGlyphQuads, shapedTextOrientations.right); } @@ -89,12 +56,97 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, } if (shapedTextOrientations.vertical) { - writingModes |= WritingModeType::Vertical; verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.vertical, textOffset, layout, textPlacement, positions); } +} +bool SymbolInstanceSharedData::empty() const { + return rightJustifiedGlyphQuads.empty() && centerJustifiedGlyphQuads.empty() && leftJustifiedGlyphQuads.empty() && verticalGlyphQuads.empty(); +} + +SymbolInstance::SymbolInstance(Anchor& anchor_, + std::shared_ptr sharedData_, + const ShapedTextOrientations& shapedTextOrientations, + const optional& shapedIcon, + const float textBoxScale_, + const float textPadding, + const SymbolPlacementType textPlacement, + const std::array& textOffset_, + const float iconBoxScale, + const float iconPadding, + const std::array& iconOffset_, + const IndexedSubfeature& indexedFeature, + const std::size_t layoutFeatureIndex_, + const std::size_t dataFeatureIndex_, + std::u16string key_, + const float overscaling, + const float rotate, + float radialTextOffset_) : + sharedData(std::move(sharedData_)), + anchor(anchor_), // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap - hasText = !rightJustifiedGlyphQuads.empty() || !centerJustifiedGlyphQuads.empty() || !leftJustifiedGlyphQuads.empty() || !verticalGlyphQuads.empty(); + hasText(!sharedData->empty()), + hasIcon(shapedIcon), + // Create the collision features that will be used to check whether this symbol instance can be placed + // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature + textCollisionFeature(sharedData->line, anchor, getAnyShaping(shapedTextOrientations), textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, rotate), + iconCollisionFeature(sharedData->line, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, rotate), + writingModes(WritingModeType::None), + layoutFeatureIndex(layoutFeatureIndex_), + dataFeatureIndex(dataFeatureIndex_), + textOffset(textOffset_), + iconOffset(iconOffset_), + key(std::move(key_)), + textBoxScale(textBoxScale_), + radialTextOffset(radialTextOffset_), + singleLine(shapedTextOrientations.singleLine) { + + rightJustifiedGlyphQuadsSize = sharedData->rightJustifiedGlyphQuads.size(); + centerJustifiedGlyphQuadsSize = sharedData->centerJustifiedGlyphQuads.size(); + leftJustifiedGlyphQuadsSize = sharedData->leftJustifiedGlyphQuads.size(); + verticalGlyphQuadsSize = sharedData->verticalGlyphQuads.size(); + + if (rightJustifiedGlyphQuadsSize || centerJustifiedGlyphQuadsSize || leftJustifiedGlyphQuadsSize) { + writingModes |= WritingModeType::Horizontal; + } + + if (verticalGlyphQuadsSize) { + writingModes |= WritingModeType::Vertical; + } +} + +const GeometryCoordinates& SymbolInstance::line() const { + assert(sharedData); + return sharedData->line; +} + +const SymbolQuads& SymbolInstance::rightJustifiedGlyphQuads() const { + assert(sharedData); + return sharedData->rightJustifiedGlyphQuads; +} + +const SymbolQuads& SymbolInstance::leftJustifiedGlyphQuads() const { + assert(sharedData); + return sharedData->leftJustifiedGlyphQuads; +} + +const SymbolQuads& SymbolInstance::centerJustifiedGlyphQuads() const { + assert(sharedData); + return sharedData->centerJustifiedGlyphQuads; +} + +const SymbolQuads& SymbolInstance::verticalGlyphQuads() const { + assert(sharedData); + return sharedData->verticalGlyphQuads; +} + +const optional& SymbolInstance::iconQuad() const { + assert(sharedData); + return sharedData->iconQuad; +} + +void SymbolInstance::releaseSharedData() { + sharedData.reset(); } optional SymbolInstance::getDefaultHorizontalPlacedTextIndex() const { diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 95159deeb..48bb2f0cb 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -21,23 +21,39 @@ struct ShapedTextOrientations { bool singleLine = false; }; +struct SymbolInstanceSharedData { + SymbolInstanceSharedData(GeometryCoordinates line, + const ShapedTextOrientations& shapedTextOrientations, + const optional& shapedIcon, + const style::SymbolLayoutProperties::Evaluated& layout, + const float layoutTextSize, + const style::SymbolPlacementType textPlacement, + const std::array& textOffset, + const GlyphPositions& positions); + bool empty() const; + GeometryCoordinates line; + // Note: When singleLine == true, only `rightJustifiedGlyphQuads` is populated. + SymbolQuads rightJustifiedGlyphQuads; + SymbolQuads centerJustifiedGlyphQuads; + SymbolQuads leftJustifiedGlyphQuads; + SymbolQuads verticalGlyphQuads; + optional iconQuad; +}; + class SymbolInstance { public: - SymbolInstance(Anchor& anchor, - GeometryCoordinates line, + SymbolInstance(Anchor& anchor_, + std::shared_ptr sharedData, const ShapedTextOrientations& shapedTextOrientations, - optional shapedIcon, - const style::SymbolLayoutProperties::Evaluated&, - const float layoutTextSize, + const optional& shapedIcon, const float textBoxScale, const float textPadding, - style::SymbolPlacementType textPlacement, - const std::array textOffset, + const style::SymbolPlacementType textPlacement, + const std::array& textOffset, const float iconBoxScale, const float iconPadding, - const std::array iconOffset, - const GlyphPositions&, - const IndexedSubfeature&, + const std::array& iconOffset, + const IndexedSubfeature& indexedFeature, const std::size_t layoutFeatureIndex, const std::size_t dataFeatureIndex, std::u16string key, @@ -46,18 +62,27 @@ public: float radialTextOffset); optional getDefaultHorizontalPlacedTextIndex() const; + const GeometryCoordinates& line() const; + const SymbolQuads& rightJustifiedGlyphQuads() const; + const SymbolQuads& leftJustifiedGlyphQuads() const; + const SymbolQuads& centerJustifiedGlyphQuads() const; + const SymbolQuads& verticalGlyphQuads() const; + const optional& iconQuad() const; + void releaseSharedData(); + +private: + std::shared_ptr sharedData; + +public: Anchor anchor; - GeometryCoordinates line; bool hasText; bool hasIcon; - // Note: When singleLine == true, only `rightJustifiedGlyphQuads` is populated. - SymbolQuads rightJustifiedGlyphQuads; - SymbolQuads centerJustifiedGlyphQuads; - SymbolQuads leftJustifiedGlyphQuads; - SymbolQuads verticalGlyphQuads; + std::size_t rightJustifiedGlyphQuadsSize; + std::size_t centerJustifiedGlyphQuadsSize; + std::size_t leftJustifiedGlyphQuadsSize; + std::size_t verticalGlyphQuadsSize; - optional iconQuad; CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; WritingModeType writingModes; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 21642b01e..d269ca414 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -397,7 +397,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const SymbolFeature& feature, const ShapedTextOrientations& shapedTextOrientations, - optional shapedIcon, + const optional& shapedIcon, const GlyphPositions& glyphPositions, Point offset) { const float minScale = 0.5f; @@ -433,7 +433,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const auto evaluatedLayoutProperties = layout->evaluate(zoom, feature); IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); - auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { + auto addSymbolInstance = [&] (Anchor& anchor, std::shared_ptr sharedData) { + assert(sharedData); const bool anchorInsideTile = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT; if (mode == MapMode::Tile || anchorInsideTile) { @@ -442,20 +443,26 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, // In tiled rendering mode, add all symbols in the buffers so that we can: // (1) render symbols that overlap into this tile // (2) approximate collision detection effects from neighboring symbols - symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, - evaluatedLayoutProperties, layoutTextSize, + symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations, shapedIcon, textBoxScale, textPadding, textPlacement, textOffset, - iconBoxScale, iconPadding, iconOffset, - glyphPositions, indexedFeature, layoutFeatureIndex, feature.index, - feature.formattedText ? feature.formattedText->rawText() : std::u16string(), overscaling, rotation, radialTextOffset); + iconBoxScale, iconPadding, iconOffset, indexedFeature, + layoutFeatureIndex, feature.index, + feature.formattedText ? feature.formattedText->rawText() : std::u16string(), + overscaling, rotation, radialTextOffset); } }; + const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) { + return std::make_shared(std::move(line), + shapedTextOrientations, shapedIcon, evaluatedLayoutProperties, layoutTextSize, + textPlacement, textOffset, glyphPositions); + }; + const auto& type = feature.getType(); if (layout->get() == SymbolPlacementType::Line) { auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT); - for (const auto& line : clippedLines) { + for (auto& line : clippedLines) { Anchors anchors = getAnchors(line, symbolSpacing, textMaxAngle, @@ -466,10 +473,10 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, glyphSize, textMaxBoxScale, overscaling); - + auto sharedData = createSymbolInstanceSharedData(std::move(line)); for (auto& anchor : anchors) { if (!feature.formattedText || !anchorIsTooClose(feature.formattedText->rawText(), textRepeatDistance, anchor)) { - addSymbolInstance(line, anchor); + addSymbolInstance(anchor, sharedData); } } } @@ -487,7 +494,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, glyphSize, textMaxBoxScale); if (anchor) { - addSymbolInstance(line, *anchor); + addSymbolInstance(*anchor, createSymbolInstanceSharedData(line)); } } } @@ -505,18 +512,18 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, // 1 pixel worth of precision, in tile coordinates auto poi = mapbox::polylabel(poly, double(util::EXTENT / util::tileSize)); Anchor anchor(poi.x, poi.y, 0, minScale); - addSymbolInstance(polygon[0], anchor); + addSymbolInstance(anchor, createSymbolInstanceSharedData(polygon[0])); } } else if (type == FeatureType::LineString) { for (const auto& line : feature.geometry) { Anchor anchor(line[0].x, line[0].y, 0, minScale); - addSymbolInstance(line, anchor); + addSymbolInstance(anchor, createSymbolInstanceSharedData(line)); } } else if (type == FeatureType::Point) { for (const auto& points : feature.geometry) { for (const auto& point : points) { Anchor anchor(point.x, point.y, 0, minScale); - addSymbolInstance({point}, anchor); + addSymbolInstance(anchor, createSymbolInstanceSharedData({point})); } } } @@ -576,36 +583,36 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr lastAddedSection; if (singleLine) { optional placedTextIndex; - lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, placedTextIndex, symbolInstance.rightJustifiedGlyphQuads, lastAddedSection); + lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, placedTextIndex, symbolInstance.rightJustifiedGlyphQuads(), lastAddedSection); symbolInstance.placedRightTextIndex = placedTextIndex; symbolInstance.placedCenterTextIndex = placedTextIndex; symbolInstance.placedLeftTextIndex = placedTextIndex; } else { - if (!symbolInstance.rightJustifiedGlyphQuads.empty()) { - lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedRightTextIndex, symbolInstance.rightJustifiedGlyphQuads, lastAddedSection); + if (symbolInstance.rightJustifiedGlyphQuadsSize) { + lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedRightTextIndex, symbolInstance.rightJustifiedGlyphQuads(), lastAddedSection); } - if (!symbolInstance.centerJustifiedGlyphQuads.empty()) { - lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedCenterTextIndex, symbolInstance.centerJustifiedGlyphQuads, lastAddedSection); + if (symbolInstance.centerJustifiedGlyphQuadsSize) { + lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedCenterTextIndex, symbolInstance.centerJustifiedGlyphQuads(), lastAddedSection); } - if (!symbolInstance.leftJustifiedGlyphQuads.empty()) { - lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedLeftTextIndex, symbolInstance.leftJustifiedGlyphQuads, lastAddedSection); + if (symbolInstance.leftJustifiedGlyphQuadsSize) { + lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedLeftTextIndex, symbolInstance.leftJustifiedGlyphQuads(), lastAddedSection); } } - if (symbolInstance.writingModes & WritingModeType::Vertical) { - lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, WritingModeType::Vertical, symbolInstance.placedVerticalTextIndex, symbolInstance.verticalGlyphQuads, lastAddedSection); + if (symbolInstance.writingModes & WritingModeType::Vertical && symbolInstance.verticalGlyphQuadsSize) { + lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, WritingModeType::Vertical, symbolInstance.placedVerticalTextIndex, symbolInstance.verticalGlyphQuads(), lastAddedSection); } assert(lastAddedSection); // True, as hasText == true; updatePaintPropertiesForSection(*bucket, feature, *lastAddedSection); } if (hasIcon) { - if (symbolInstance.iconQuad) { + if (symbolInstance.hasIcon) { const Range sizeData = bucket->iconSizeBinder->getVertexSizeData(feature); bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, - symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line, std::vector()); + symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line(), std::vector()); symbolInstance.placedIconIndex = bucket->icon.placedSymbols.size() - 1; PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back(); - iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad, + iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad(), symbolInstance.anchor, iconSymbol, feature.sortKey); for (auto& pair : bucket->paintProperties) { @@ -613,6 +620,7 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr shapedIcon, + const optional& shapedIcon, const GlyphPositions&, Point textOffset); diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 1795b0dcb..b7d2189af 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -547,28 +547,28 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState if (symbolInstance.hasText) { auto opacityVertex = SymbolSDFTextProgram::opacityVertex(opacityState.text.placed, opacityState.text.opacity); if (symbolInstance.placedRightTextIndex) { - for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuads.size() * 4; i++) { + for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuadsSize * 4; i++) { bucket.text.opacityVertices.emplace_back(opacityVertex); } PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedRightTextIndex]; placed.hidden = opacityState.isHidden(); } if (symbolInstance.placedCenterTextIndex && !symbolInstance.singleLine) { - for (size_t i = 0; i < symbolInstance.centerJustifiedGlyphQuads.size() * 4; i++) { + for (size_t i = 0; i < symbolInstance.centerJustifiedGlyphQuadsSize * 4; i++) { bucket.text.opacityVertices.emplace_back(opacityVertex); } PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedCenterTextIndex]; placed.hidden = opacityState.isHidden(); } if (symbolInstance.placedLeftTextIndex && !symbolInstance.singleLine) { - for (size_t i = 0; i < symbolInstance.leftJustifiedGlyphQuads.size() * 4; i++) { + for (size_t i = 0; i < symbolInstance.leftJustifiedGlyphQuadsSize * 4; i++) { bucket.text.opacityVertices.emplace_back(opacityVertex); } PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedLeftTextIndex]; placed.hidden = opacityState.isHidden(); } if (symbolInstance.placedVerticalTextIndex) { - for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) { + for (size_t i = 0; i < symbolInstance.verticalGlyphQuadsSize * 4; i++) { bucket.text.opacityVertices.emplace_back(opacityVertex); } bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden(); @@ -581,12 +581,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState } if (symbolInstance.hasIcon) { auto opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity); - if (symbolInstance.iconQuad) { - bucket.icon.opacityVertices.emplace_back(opacityVertex); - bucket.icon.opacityVertices.emplace_back(opacityVertex); - bucket.icon.opacityVertices.emplace_back(opacityVertex); - bucket.icon.opacityVertices.emplace_back(opacityVertex); - } + bucket.icon.opacityVertices.emplace_back(opacityVertex); + bucket.icon.opacityVertices.emplace_back(opacityVertex); + bucket.icon.opacityVertices.emplace_back(opacityVertex); + bucket.icon.opacityVertices.emplace_back(opacityVertex); if (symbolInstance.placedIconIndex) { bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden(); }