From 3e6ab2236affbdf7d3e0cb892e504bad3c92c586 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Wed, 13 Mar 2019 23:17:14 +0200 Subject: [core] Introduce variable text placement for point labels - Render part --- src/mbgl/programs/symbol_program.cpp | 7 +- src/mbgl/programs/symbol_program.hpp | 2 + src/mbgl/renderer/layers/render_symbol_layer.cpp | 92 ++++++++++++++++++++++-- src/mbgl/renderer/paint_parameters.hpp | 2 + src/mbgl/text/placement.hpp | 6 ++ 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 2300dedff3..43eae19cde 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -42,6 +42,7 @@ std::unique_ptr SymbolSizeBinder::create(const float tileZoom, template Values makeValues(const bool isText, + const bool hasVariablePacement, const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, @@ -71,7 +72,7 @@ Values makeValues(const bool isText, const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine; mat4 labelPlaneMatrix; - if (alongLine) { + if (alongLine || (isText && hasVariablePacement)) { // For labels that follow lines the first part of the projection is handled on the cpu. // Pass an identity matrix because no transformation needs to be done in the vertex shader. matrix::identity(labelPlaneMatrix); @@ -106,6 +107,7 @@ Values makeValues(const bool isText, SymbolIconProgram::LayoutUniformValues SymbolIconProgram::layoutUniformValues(const bool isText, + const bool hasVariablePacement, const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, @@ -115,6 +117,7 @@ SymbolIconProgram::layoutUniformValues(const bool isText, const float symbolFadeChange) { return makeValues( isText, + hasVariablePacement, values, texsize, pixelsToGLUnits, @@ -128,6 +131,7 @@ SymbolIconProgram::layoutUniformValues(const bool isText, template typename SymbolSDFProgram::LayoutUniformValues SymbolSDFProgram::layoutUniformValues(const bool isText, + const bool hasVariablePacement, const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, @@ -142,6 +146,7 @@ SymbolSDFProgram::layoutUniformValues(const bool isText, return makeValues::LayoutUniformValues>( isText, + hasVariablePacement, values, texsize, pixelsToGLUnits, diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 383f5162d8..14290fe348 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -368,6 +368,7 @@ public: using SymbolProgram::SymbolProgram; static LayoutUniformValues layoutUniformValues(const bool isText, + const bool hasVariablePacement, const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, @@ -437,6 +438,7 @@ public: using BaseProgram::BaseProgram; static LayoutUniformValues layoutUniformValues(const bool isText, + const bool hasVariablePacement, const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 31d92dd414..ef18a21005 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,6 +25,19 @@ namespace mbgl { using namespace style; +namespace { +Point calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) { + AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor); + float shiftX = -(alignment.horizontalAlign - 0.5f) * width; + float shiftY = -(alignment.verticalAlign - 0.5f) * height; + Point offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset); + return Point( + (shiftX / textBoxScale + offset.x) * renderTextSize, + (shiftY / textBoxScale + offset.y) * renderTextSize + ); +} +} // namespace + RenderSymbolLayer::RenderSymbolLayer(Immutable _impl) : RenderLayer(std::move(_impl)), unevaluated(impl().paint.untransitioned()) { @@ -172,7 +187,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { if (bucket.sdfIcons) { if (values.hasHalo) { draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), bucket.icon, bucket.iconSizeBinder, values, @@ -185,7 +200,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { if (values.hasFill) { draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), bucket.icon, bucket.iconSizeBinder, values, @@ -197,7 +212,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { } } else { draw(parameters.programs.getSymbolLayerPrograms().symbolIcon, - SymbolIconProgram::layoutUniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), + SymbolIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), bucket.icon, bucket.iconSizeBinder, values, @@ -215,6 +230,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { auto values = textPropertyValues(evaluated_, layout); const auto& paintPropertyValues = textPaintProperties(evaluated_); + bool hasVariablePacement = false; const bool alongLine = layout.get() != SymbolPlacementType::Point && layout.get() == AlignmentType::Map; @@ -229,13 +245,79 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { parameters.state); parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); + } else if (!layout.get().empty()) { + bucket.text.dynamicVertices.clear(); + + const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(parameters.state.getZoom()); + const float tileScale = std::pow(2, parameters.state.getZoom() - tile.tile.id.overscaledZ); + const bool rotateWithMap = layout.get() == AlignmentType::Map; + const bool pitchWithMap = layout.get() == AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, parameters.state.getZoom()); + const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, parameters.state, pixelsToTileUnits); + + for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { + optional variableOffset; + if (!symbol.hidden && symbol.crossTileID != 0u) { + auto it = parameters.variableOffsets.find(symbol.crossTileID); + if (it != parameters.variableOffsets.end()) { + variableOffset = it->second; + hasVariablePacement |= true; + } + } + + if (!variableOffset) { + // These symbols are from a justification that is not being used, or a label that wasn't placed + // so we don't need to do the extra math to figure out what incremental shift to apply. + hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices); + } else { + const Point tileAnchor = symbol.anchorPoint; + const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix); + const float perspectiveRatio = 0.5f + 0.5f * (parameters.state.getCameraToCenterDistance() / projectedAnchor.second); + float renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol) * perspectiveRatio / util::ONE_EM; + if (pitchWithMap) { + // Go from size in pixels to equivalent size in tile units + renderTextSize *= bucket.tilePixelRatio / tileScale; + } + + auto shift = calculateVariableRenderShift( + (*variableOffset).anchor, + (*variableOffset).width, + (*variableOffset).height, + (*variableOffset).radialOffset, + (*variableOffset).textBoxScale, + renderTextSize); + + // Usual case is that we take the projected anchor and add the pixel-based shift + // calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent + // tile-unit based shift to the anchor before projecting to the label plane. + Point shiftedAnchor; + if (pitchWithMap) { + shiftedAnchor = project(Point(tileAnchor.x + shift.x, tileAnchor.y + shift.y), + labelPlaneMatrix).first; + } else { + if (rotateWithMap) { + auto rotated = util::rotate(shift, -parameters.state.getPitch()); + shiftedAnchor = Point(projectedAnchor.first.x + rotated.x, + projectedAnchor.first.y + rotated.y); + } else { + shiftedAnchor = Point(projectedAnchor.first.x + shift.x, + projectedAnchor.first.y + shift.y); + } + } + + for (std::size_t i = 0; i < symbol.glyphOffsets.size(); i++) { + addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices); + } + } + } + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); } const Size texsize = geometryTile.glyphAtlasTexture->size; if (values.hasHalo) { draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), bucket.text, bucket.textSizeBinder, values, @@ -248,7 +330,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { if (values.hasFill) { draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), bucket.text, bucket.textSizeBinder, values, diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index ea3b41adfe..0b729d102b 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -56,6 +57,7 @@ public: TimePoint timePoint; float pixelRatio; + std::unordered_map variableOffsets; std::array pixelsToGLUnits; algorithm::ClipIDGenerator clipIDGenerator; diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index 32310f723e..673ea59c24 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -138,4 +138,10 @@ private: CollisionGroups collisionGroups; }; +Point calculateVariableLayoutOffset(style::SymbolAnchorType anchor, + float width, + float height, + float radialOffset, + float textBoxScale); + } // namespace mbgl -- cgit v1.2.1