From 4913f92c3f4a19d05eff82e05849cf8e80f1bc65 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Thu, 8 Aug 2019 13:56:22 +0300 Subject: [PATCH 1/3] [core] Fix combination of icon-text-fit with text-variable-anchors and text-writing-mode --- src/mbgl/layout/symbol_instance.cpp | 21 ++- src/mbgl/layout/symbol_instance.hpp | 7 +- src/mbgl/layout/symbol_layout.cpp | 73 +++++--- src/mbgl/layout/symbol_layout.hpp | 2 +- src/mbgl/programs/symbol_program.cpp | 2 +- src/mbgl/renderer/buckets/symbol_bucket.cpp | 4 + src/mbgl/renderer/buckets/symbol_bucket.hpp | 7 +- .../renderer/layers/render_symbol_layer.cpp | 7 +- src/mbgl/text/placement.cpp | 139 +++++++++++---- src/mbgl/text/quads.cpp | 48 +---- src/mbgl/text/quads.hpp | 4 +- src/mbgl/text/shaping.cpp | 45 ++++- src/mbgl/text/shaping.hpp | 7 + test/text/cross_tile_symbol_index.test.cpp | 4 +- test/text/quads.test.cpp | 168 ++++++++++-------- 15 files changed, 349 insertions(+), 189 deletions(-) diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 113bf5bd355..8be025afe18 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -21,15 +21,18 @@ const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientation SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_, const ShapedTextOrientations& shapedTextOrientations, const optional& shapedIcon, + const optional& verticallyShapedIcon, const style::SymbolLayoutProperties::Evaluated& layout, - const float layoutTextSize, const style::SymbolPlacementType textPlacement, const std::array& textOffset, const GlyphPositions& positions, bool allowVerticalPlacement) : line(std::move(line_)) { // Create the quads used for rendering the icon and glyphs. if (shapedIcon) { - iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.horizontal); + iconQuad = getIconQuad(*shapedIcon, getAnyShaping(shapedTextOrientations).writingMode); + if (verticallyShapedIcon) { + verticalIconQuad = getIconQuad(*verticallyShapedIcon, shapedTextOrientations.vertical.writingMode); + } } bool singleLineInitialized = false; @@ -69,6 +72,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, std::shared_ptr sharedData_, const ShapedTextOrientations& shapedTextOrientations, const optional& shapedIcon, + const optional& verticallyShapedIcon, const float textBoxScale_, const float textPadding, const SymbolPlacementType textPlacement, @@ -107,6 +111,14 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, if (allowVerticalPlacement && shapedTextOrientations.vertical) { const float verticalPointLabelAngle = 90.0f; verticalTextCollisionFeature = CollisionFeature(line(), anchor, shapedTextOrientations.vertical, textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, textRotation + verticalPointLabelAngle); + if (verticallyShapedIcon) { + verticalIconCollisionFeature = CollisionFeature(sharedData->line, + anchor, + verticallyShapedIcon, + iconBoxScale, iconPadding, + indexedFeature, + iconRotation + verticalPointLabelAngle); + } } rightJustifiedGlyphQuadsSize = sharedData->rightJustifiedGlyphQuads.size(); @@ -153,6 +165,11 @@ const optional& SymbolInstance::iconQuad() const { return sharedData->iconQuad; } +const optional& SymbolInstance::verticalIconQuad() const { + assert(sharedData); + return sharedData->verticalIconQuad; +} + void SymbolInstance::releaseSharedData() { sharedData.reset(); } diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 2c6aad653f0..60883c12dbf 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -25,8 +25,8 @@ struct SymbolInstanceSharedData { SymbolInstanceSharedData(GeometryCoordinates line, const ShapedTextOrientations& shapedTextOrientations, const optional& shapedIcon, + const optional& verticallyShapedIcon, const style::SymbolLayoutProperties::Evaluated& layout, - const float layoutTextSize, const style::SymbolPlacementType textPlacement, const std::array& textOffset, const GlyphPositions& positions, @@ -39,6 +39,7 @@ struct SymbolInstanceSharedData { SymbolQuads leftJustifiedGlyphQuads; SymbolQuads verticalGlyphQuads; optional iconQuad; + optional verticalIconQuad; }; class SymbolInstance { @@ -47,6 +48,7 @@ class SymbolInstance { std::shared_ptr sharedData, const ShapedTextOrientations& shapedTextOrientations, const optional& shapedIcon, + const optional& verticallyShapedIcon, const float textBoxScale, const float textPadding, const style::SymbolPlacementType textPlacement, @@ -71,6 +73,7 @@ class SymbolInstance { const SymbolQuads& centerJustifiedGlyphQuads() const; const SymbolQuads& verticalGlyphQuads() const; const optional& iconQuad() const; + const optional& verticalIconQuad() const; void releaseSharedData(); private: @@ -89,6 +92,7 @@ class SymbolInstance { CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; optional verticalTextCollisionFeature = nullopt; + optional verticalIconCollisionFeature = nullopt; WritingModeType writingModes; std::size_t layoutFeatureIndex; // Index into the set of features included at layout time std::size_t dataFeatureIndex; // Index into the underlying tile data feature set @@ -101,6 +105,7 @@ class SymbolInstance { optional placedLeftTextIndex; optional placedVerticalTextIndex; optional placedIconIndex; + optional placedVerticalIconIndex; float textBoxScale; float radialTextOffset; bool singleLine; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 9459dcb716a..6a13bb22ae8 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -419,7 +419,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions // if either shapedText or icon position is present, add the feature if (getDefaultHorizontalShaping(shapedTextOrientations) || shapedIcon) { - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions, textOffset); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, std::move(shapedIcon), glyphPositions, textOffset); } feature.geometry.clear(); @@ -431,7 +431,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const SymbolFeature& feature, const ShapedTextOrientations& shapedTextOrientations, - const optional& shapedIcon, + optional shapedIcon, const GlyphPositions& glyphPositions, Point offset) { const float minScale = 0.5f; @@ -467,6 +467,22 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const float textRepeatDistance = symbolSpacing / 2; const auto evaluatedLayoutProperties = layout->evaluate(zoom, feature); IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); + const bool hasIconTextFit = evaluatedLayoutProperties.get() != IconTextFitType::None; + + // Adjust shaped icon size when icon-text-fit is used. + optional verticallyShapedIcon; + if (shapedIcon && hasIconTextFit) { + // Create vertically shaped icon for vertical writing mode if needed. + if (allowVerticalPlacement && shapedTextOrientations.vertical) { + verticallyShapedIcon = shapedIcon; + verticallyShapedIcon->fitIconToText(evaluatedLayoutProperties, + shapedTextOrientations.vertical, + layoutTextSize); + } + shapedIcon->fitIconToText(evaluatedLayoutProperties, + getDefaultHorizontalShaping(shapedTextOrientations), + layoutTextSize); + } auto addSymbolInstance = [&] (Anchor& anchor, std::shared_ptr sharedData) { assert(sharedData); @@ -478,7 +494,8 @@ 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, std::move(sharedData), shapedTextOrientations, shapedIcon, + symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations, + shapedIcon, verticallyShapedIcon, textBoxScale, textPadding, textPlacement, textOffset, iconBoxScale, iconPadding, iconOffset, indexedFeature, layoutFeatureIndex, feature.index, @@ -489,7 +506,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) { return std::make_shared(std::move(line), - shapedTextOrientations, shapedIcon, evaluatedLayoutProperties, layoutTextSize, + shapedTextOrientations, shapedIcon, verticallyShapedIcon, evaluatedLayoutProperties, textPlacement, textOffset, glyphPositions, allowVerticalPlacement); }; @@ -617,6 +634,32 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr sizeData = bucket->iconSizeBinder->getVertexSizeData(feature); + const auto placeIcon = [&] (const SymbolQuad& iconQuad, auto& index, const WritingModeType writingMode) { + bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.iconOffset, writingMode, symbolInstance.line(), std::vector()); + index = bucket->icon.placedSymbols.size() - 1; + PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back(); + iconSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0; + iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, iconQuad, + symbolInstance.anchor, iconSymbol, feature.sortKey); + }; + + placeIcon(*symbolInstance.iconQuad(), symbolInstance.placedIconIndex, WritingModeType::None); + if (symbolInstance.verticalIconQuad()) { + placeIcon(*symbolInstance.verticalIconQuad(), symbolInstance.placedVerticalIconIndex, WritingModeType::Vertical); + } + + for (auto& pair : bucket->paintProperties) { + pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {}); + } + } + } + if (hasText && feature.formattedText) { optional lastAddedSection; if (singleLine) { @@ -643,21 +686,6 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr 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.placedIconIndex = bucket->icon.placedSymbols.size() - 1; - PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back(); - iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad(), - symbolInstance.anchor, iconSymbol, feature.sortKey); - - for (auto& pair : bucket->paintProperties) { - pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {}); - } - } - } symbolInstance.releaseSharedData(); } @@ -693,9 +721,9 @@ std::size_t SymbolLayout::addSymbolGlyphQuads(SymbolBucket& bucket, optional lastAddedSection) { const Range sizeData = bucket.textSizeBinder->getVertexSizeData(feature); const bool hasFormatSectionOverrides = bucket.hasFormatSectionOverrides(); - + const auto& placedIconIndex = writingMode == WritingModeType::Vertical ? symbolInstance.placedVerticalIconIndex : symbolInstance.placedIconIndex; bucket.text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, - symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor)); + symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor), placedIconIndex); placedIndex = bucket.text.placedSymbols.size() - 1; PlacedSymbol& placedSymbol = bucket.text.placedSymbols.back(); placedSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0; @@ -837,6 +865,9 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) { if (symbolInstance.verticalTextCollisionFeature) { populateCollisionBox(*symbolInstance.verticalTextCollisionFeature); } + if (symbolInstance.verticalIconCollisionFeature) { + populateCollisionBox(*symbolInstance.verticalIconCollisionFeature); + } populateCollisionBox(symbolInstance.iconCollisionFeature); } } diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 70a3482644d..6cc21c6d91a 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -51,7 +51,7 @@ class SymbolLayout final : public Layout { void addFeature(const size_t, const SymbolFeature&, const ShapedTextOrientations& shapedTextOrientations, - const optional& shapedIcon, + optional shapedIcon, const GlyphPositions&, Point textOffset); diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 3633bd7c2ac..bfc01336768 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -67,7 +67,7 @@ Values makeValues(const bool isText, const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine; mat4 labelPlaneMatrix; - if (alongLine || (isText && hasVariablePacement)) { + if (alongLine || 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); diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 21a78704731..681a492e734 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -228,6 +228,10 @@ void SymbolBucket::sortFeatures(const float angle) { if (symbolInstance.placedIconIndex) { addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]); } + + if (symbolInstance.placedVerticalIconIndex) { + addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedVerticalIconIndex]); + } } } diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index f47ced8331d..20c0c5b7905 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -21,9 +21,9 @@ class CrossTileSymbolLayerIndex; class PlacedSymbol { public: PlacedSymbol(Point anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_, - std::array lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector tileDistances_) : + std::array lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector tileDistances_, optional placedIconIndex_ = nullopt) : anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_), - lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0) + lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0), placedIconIndex(std::move(placedIconIndex_)) { } Point anchorPoint; @@ -43,6 +43,9 @@ class PlacedSymbol { // placement for orientation variants. optional placedOrientation; float angle = 0; + + // Reference to placed icon, only applicable for text symbols. + optional placedIconIndex; }; class SymbolBucket final : public Bucket { diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 97332413934..ffb32b9746e 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -131,11 +131,12 @@ void drawIcon(const DrawFn& draw, : gfx::TextureFilterType::Nearest }; const Size& iconSize = tile.getIconAtlasTexture().size; + const bool variablePlacedIcon = bucket.hasVariablePlacement && layout.get() != IconTextFitType::None; if (bucket.sdfIcons) { if (values.hasHalo) { draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), bucket.icon, iconSegments, bucket.iconSizeBinder, @@ -149,7 +150,7 @@ void drawIcon(const DrawFn& draw, if (values.hasFill) { draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), bucket.icon, iconSegments, bucket.iconSizeBinder, @@ -162,7 +163,7 @@ void drawIcon(const DrawFn& draw, } } else { draw(parameters.programs.getSymbolLayerPrograms().symbolIcon, - SymbolIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), + SymbolIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), bucket.icon, iconSegments, bucket.iconSizeBinder, diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 27c4913c63e..1428cf7792a 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -157,6 +157,7 @@ void Placement::placeBucket( std::vector variableTextAnchors = layout.get(); const bool rotateWithMap = layout.get() == style::AlignmentType::Map; const bool pitchWithMap = layout.get() == style::AlignmentType::Map; + const bool hasIconTextFit = layout.get() != style::IconTextFitType::None; const bool hasCollisionCircleData = bucket.hasCollisionCircleData(); const bool zOrderByViewportY = layout.get() == style::SymbolZOrderType::ViewportY; @@ -179,7 +180,9 @@ void Placement::placeBucket( bool placeIcon = false; bool offscreen = true; std::pair placed{ false, false }; - std::pair placedVertical{ false, false }; + std::pair placedVerticalText{ false, false }; + std::pair placedVerticalIcon{ false, false }; + Point shift{0.0f, 0.0f}; optional 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 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(), + pitchWithMap, + params.showCollisionBoxes, avoidEdges, + collisionGroup.second, iconBoxes); + }; - auto placedIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {}, - posMatrix, iconLabelPlaneMatrix, pixelRatio, - placedSymbol, scale, fontSize, - layout.get(), - pitchWithMap, - params.showCollisionBoxes, avoidEdges, collisionGroup.second, iconBoxes); + std::pair 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(), bucket.bucketInstanceId, collisionGroup.first); } else { collisionIndex.insertFeature(symbolInstance.textCollisionFeature, textBoxes, layout.get(), bucket.bucketInstanceId, collisionGroup.first); @@ -393,7 +409,11 @@ void Placement::placeBucket( } if (placeIcon) { - collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get(), bucket.bucketInstanceId, collisionGroup.first); + if (placedVerticalIcon.first && symbolInstance.verticalIconCollisionFeature) { + collisionIndex.insertFeature(*symbolInstance.verticalIconCollisionFeature, iconBoxes, layout.get(), bucket.bucketInstanceId, collisionGroup.first); + } else { + collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get(), 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() != SymbolPlacementType::Point; + const bool hasVariableAnchors = !layout.get().empty() && bucket.hasTextData(); + const bool updateTextFitIcon = layout.get() != 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().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() == 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>> 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; 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>{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().empty(); const bool rotateWithMap = bucket.layout->get() == style::AlignmentType::Map; const bool pitchWithMap = bucket.layout->get() == style::AlignmentType::Map; + const bool hasIconTextFit = bucket.layout->get() != 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& 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 shift{0.0f, 0.0f}; if (feature.alongLine) { - return; + return shift; } - Point 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 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{0.0f, 0.0f}); + } } - updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed); + updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed, hasIconTextFit ? textShift : Point{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 b08c2bc0bac..281c5d99de8 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 tl; - Point tr; - Point br; - Point bl; - - if (layout.get() != 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()[0]; - auto padR = layout.get()[1]; - auto padB = layout.get()[2]; - auto padL = layout.get()[3]; - auto offsetY = layout.get() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; - auto offsetX = layout.get() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; - auto width = layout.get() == IconTextFitType::Width || layout.get() == IconTextFitType::Both ? textWidth : iconWidth; - auto height = layout.get() == IconTextFitType::Height || layout.get() == 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 tl{left, top}; + Point tr{right, top}; + Point br{right, bottom}; + Point bl{left, bottom}; const float angle = shapedIcon.angle(); @@ -88,7 +54,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, static_cast(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 1ec68189af9..145fd2b153c 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -44,9 +44,7 @@ class SymbolQuad { using SymbolQuads = std::vector; 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 textOffset, diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 7bf0e14f807..0cb9ea73a97 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& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) { +PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, + const std::array& 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() != 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()[0]; + auto padR = layout.get()[1]; + auto padB = layout.get()[2]; + auto padL = layout.get()[3]; + auto offsetY = layout.get() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; + auto offsetX = layout.get() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; + auto width = layout.get() == IconTextFitType::Width || layout.get() == IconTextFitType::Both ? textWidth : iconWidth; + auto height = layout.get() == IconTextFitType::Height || layout.get() == 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 f3a01e3caf5..60a9c718ff8 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace mbgl { @@ -52,6 +53,12 @@ class PositionedIcon { 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; } diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp index ccf83e81c82..a1385dfa8a7 100644 --- a/test/text/cross_tile_symbol_index.test.cpp +++ b/test/text/cross_tile_symbol_index.test.cpp @@ -16,9 +16,9 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) { style::SymbolPlacementType placementType = style::SymbolPlacementType::Point; auto sharedData = std::make_shared(std::move(line), - shaping, nullopt, layout_, 0.0f, placementType, + shaping, nullopt, nullopt, layout_, placementType, textOffset, positions, false); - return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, 0.0f, false); + return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, 0.0f, false); } diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp index c032d58b888..7aaeb4870df 100644 --- a/test/text/quads.test.cpp +++ b/test/text/quads.test.cpp @@ -20,10 +20,9 @@ TEST(getIconQuads, normal) { auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, SymbolAnchorType::Center, 0); GeometryCoordinates line; - Shaping shapedText; SymbolQuad quad = - getIconQuad(shapedIcon, layout, 16.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); EXPECT_EQ(quad.tl.x, -14); EXPECT_EQ(quad.tl.y, -10); @@ -42,8 +41,6 @@ TEST(getIconQuads, style) { style::Image::Impl("test", PremultipliedImage({1,1}), 1.0) }; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - GeometryCoordinates line; Shaping shapedText; shapedText.top = -10.0f; @@ -54,9 +51,10 @@ TEST(getIconQuads, style) { // none { + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); SymbolLayoutProperties::Evaluated layout; SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); EXPECT_EQ(quad.tl.x, -19.5); EXPECT_EQ(quad.tl.y, -19.5); @@ -73,16 +71,18 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 24.0f; layout.get() = IconTextFitType::Width; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 24.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 24.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); - EXPECT_EQ(quad.tl.x, -60); + EXPECT_EQ(quad.tl.x, -61); EXPECT_EQ(quad.tl.y, 0); - EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.x, 21); EXPECT_EQ(quad.tr.y, 0); - EXPECT_EQ(quad.bl.x, -60); + EXPECT_EQ(quad.bl.x, -61); EXPECT_EQ(quad.bl.y, 20); - EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.x, 21); EXPECT_EQ(quad.br.y, 20); } @@ -91,16 +91,18 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 12.0f; layout.get() = IconTextFitType::Width; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); - EXPECT_EQ(quad.tl.x, -30); + EXPECT_EQ(quad.tl.x, -31); EXPECT_EQ(quad.tl.y, -5); - EXPECT_EQ(quad.tr.x, 10); + EXPECT_EQ(quad.tr.x, 11); EXPECT_EQ(quad.tr.y, -5); - EXPECT_EQ(quad.bl.x, -30); + EXPECT_EQ(quad.bl.x, -31); EXPECT_EQ(quad.bl.y, 15); - EXPECT_EQ(quad.br.x, 10); + EXPECT_EQ(quad.br.x, 11); EXPECT_EQ(quad.br.y, 15); } @@ -113,16 +115,18 @@ TEST(getIconQuads, style) { layout.get()[1] = 10.0f; layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); - EXPECT_EQ(quad.tl.x, -40); + EXPECT_EQ(quad.tl.x, -41); EXPECT_EQ(quad.tl.y, -10); - EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.x, 21); EXPECT_EQ(quad.tr.y, -10); - EXPECT_EQ(quad.bl.x, -40); + EXPECT_EQ(quad.bl.x, -41); EXPECT_EQ(quad.bl.y, 20); - EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.x, 21); EXPECT_EQ(quad.br.y, 20); } @@ -131,17 +135,19 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 24.0f; layout.get() = IconTextFitType::Height; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 24.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 24.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); EXPECT_EQ(quad.tl.x, -30); - EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tl.y, -11); EXPECT_EQ(quad.tr.x, -10); - EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.tr.y, -11); EXPECT_EQ(quad.bl.x, -30); - EXPECT_EQ(quad.bl.y, 30); + EXPECT_EQ(quad.bl.y, 31); EXPECT_EQ(quad.br.x, -10); - EXPECT_EQ(quad.br.y, 30); + EXPECT_EQ(quad.br.y, 31); } // height x textSize @@ -149,17 +155,19 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 12.0f; layout.get() = IconTextFitType::Height; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); EXPECT_EQ(quad.tl.x, -20); - EXPECT_EQ(quad.tl.y, -5); + EXPECT_EQ(quad.tl.y, -6); EXPECT_EQ(quad.tr.x, 0); - EXPECT_EQ(quad.tr.y, -5); + EXPECT_EQ(quad.tr.y, -6); EXPECT_EQ(quad.bl.x, -20); - EXPECT_EQ(quad.bl.y, 15); + EXPECT_EQ(quad.bl.y, 16); EXPECT_EQ(quad.br.x, 0); - EXPECT_EQ(quad.br.y, 15); + EXPECT_EQ(quad.br.y, 16); } // height x textSize + padding @@ -171,17 +179,19 @@ TEST(getIconQuads, style) { layout.get()[1] = 10.0f; layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); + getIconQuad(shapedIcon, WritingModeType::Horizontal); EXPECT_EQ(quad.tl.x, -30); - EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tl.y, -11); EXPECT_EQ(quad.tr.x, 10); - EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.tr.y, -11); EXPECT_EQ(quad.bl.x, -30); - EXPECT_EQ(quad.bl.y, 20); + EXPECT_EQ(quad.bl.y, 21); EXPECT_EQ(quad.br.x, 10); - EXPECT_EQ(quad.br.y, 20); + EXPECT_EQ(quad.br.y, 21); } // both @@ -189,17 +199,19 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 24.0f; layout.get() = IconTextFitType::Both; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 24.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 24.0f, shapedText); - - EXPECT_EQ(quad.tl.x, -60); - EXPECT_EQ(quad.tl.y, -10); - EXPECT_EQ(quad.tr.x, 20); - EXPECT_EQ(quad.tr.y, -10); - EXPECT_EQ(quad.bl.x, -60); - EXPECT_EQ(quad.bl.y, 30); - EXPECT_EQ(quad.br.x, 20); - EXPECT_EQ(quad.br.y, 30); + getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_EQ(quad.tl.x, -61); + EXPECT_EQ(quad.tl.y, -11); + EXPECT_EQ(quad.tr.x, 21); + EXPECT_EQ(quad.tr.y, -11); + EXPECT_EQ(quad.bl.x, -61); + EXPECT_EQ(quad.bl.y, 31); + EXPECT_EQ(quad.br.x, 21); + EXPECT_EQ(quad.br.y, 31); } // both x textSize @@ -207,17 +219,19 @@ TEST(getIconQuads, style) { SymbolLayoutProperties::Evaluated layout; layout.get() = 12.0f; layout.get() = IconTextFitType::Both; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); - - EXPECT_EQ(quad.tl.x, -30); - EXPECT_EQ(quad.tl.y, -5); - EXPECT_EQ(quad.tr.x, 10); - EXPECT_EQ(quad.tr.y, -5); - EXPECT_EQ(quad.bl.x, -30); - EXPECT_EQ(quad.bl.y, 15); - EXPECT_EQ(quad.br.x, 10); - EXPECT_EQ(quad.br.y, 15); + getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_EQ(quad.tl.x, -31); + EXPECT_EQ(quad.tl.y, -6); + EXPECT_EQ(quad.tr.x, 11); + EXPECT_EQ(quad.tr.y, -6); + EXPECT_EQ(quad.bl.x, -31); + EXPECT_EQ(quad.bl.y, 16); + EXPECT_EQ(quad.br.x, 11); + EXPECT_EQ(quad.br.y, 16); } // both x textSize + padding @@ -229,17 +243,19 @@ TEST(getIconQuads, style) { layout.get()[1] = 10.0f; layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); - - EXPECT_EQ(quad.tl.x, -40); - EXPECT_EQ(quad.tl.y, -10); - EXPECT_EQ(quad.tr.x, 20); - EXPECT_EQ(quad.tr.y, -10); - EXPECT_EQ(quad.bl.x, -40); - EXPECT_EQ(quad.bl.y, 20); - EXPECT_EQ(quad.br.x, 20); - EXPECT_EQ(quad.br.y, 20); + getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_EQ(quad.tl.x, -41); + EXPECT_EQ(quad.tl.y, -11); + EXPECT_EQ(quad.tr.x, 21); + EXPECT_EQ(quad.tr.y, -11); + EXPECT_EQ(quad.bl.x, -41); + EXPECT_EQ(quad.bl.y, 21); + EXPECT_EQ(quad.br.x, 21); + EXPECT_EQ(quad.br.y, 21); } // both x textSize + padding t/r/b/l @@ -251,17 +267,19 @@ TEST(getIconQuads, style) { layout.get()[1] = 5.0f; layout.get()[2] = 10.0f; layout.get()[3] = 15.0f; + auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(layout, shapedText, 12.0f); SymbolQuad quad = - getIconQuad(shapedIcon, layout, 12.0f, shapedText); - - EXPECT_EQ(quad.tl.x, -45); - EXPECT_EQ(quad.tl.y, -5); - EXPECT_EQ(quad.tr.x, 15); - EXPECT_EQ(quad.tr.y, -5); - EXPECT_EQ(quad.bl.x, -45); - EXPECT_EQ(quad.bl.y, 25); - EXPECT_EQ(quad.br.x, 15); - EXPECT_EQ(quad.br.y, 25); + getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_EQ(quad.tl.x, -46); + EXPECT_EQ(quad.tl.y, -6); + EXPECT_EQ(quad.tr.x, 16); + EXPECT_EQ(quad.tr.y, -6); + EXPECT_EQ(quad.bl.x, -46); + EXPECT_EQ(quad.bl.y, 26); + EXPECT_EQ(quad.br.x, 16); + EXPECT_EQ(quad.br.y, 26); } } From 04277c7992a22540f0058879a0afa964adfb1699 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Thu, 15 Aug 2019 18:29:21 +0300 Subject: [PATCH 2/3] [core] Unskip icon-text-fit render tests --- platform/node/test/ignores.json | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index fa8ec6cc4ba..0595d61297b 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -100,17 +100,5 @@ "query-tests/fill-extrusion/sort-rotated": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/sort": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/top-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", - "query-tests/regressions/mapbox-gl-js#7883": "https://github.com/mapbox/mapbox-gl-native/issues/14585", - "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/height-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/placement-line": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/regressions/mapbox-gl-js#5631": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/text-variable-anchor/all-anchors-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/text-variable-anchor/icon-text-fit-collision-box": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346", - "render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346" + "query-tests/regressions/mapbox-gl-js#7883": "https://github.com/mapbox/mapbox-gl-native/issues/14585" } From 0cf61b7eeca02ca40e10e64a23ad3414291c8f41 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Tue, 20 Aug 2019 11:13:18 +0300 Subject: [PATCH 3/3] [ios][macos][android][node] Update changelog for icon-text-fit fixes --- platform/android/CHANGELOG.md | 1 + platform/ios/CHANGELOG.md | 5 +++++ platform/macos/CHANGELOG.md | 1 + platform/node/CHANGELOG.md | 1 + 4 files changed, 8 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 051be7c5bb6..8d0030b3974 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Fixed use of objects after moving, potentially causing crashes. [#15408](https://github.com/mapbox/mapbox-gl-native/pull/1540) - Fixed a possible crash that could be caused by invoking the wrong layer implementation casting function [#15398](https://github.com/mapbox/mapbox-gl-native/pull/15398). - Font lookup on pre lollipop devices failed, provide default font list instead [#15410](https://github.com/mapbox/mapbox-gl-native/pull/15410). + - Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer [#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367). ## 8.3.0-alpha.3 - August 15, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-alpha.2...android-v8.3.0-alpha.3) since [Mapbox Maps SDK for Android v8.3.0-alpha.2](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-alpha.2): diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 69b45e314a4..37612a89b84 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## master + +### Styles and rendering +* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)) + ## 5.3.0 ### Styles and rendering diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index d5092be1db5..377be60c6f0 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -11,6 +11,7 @@ * Fixed rendering layers after fill-extrusion regression caused by optimization of fill-extrusion rendering. ([#15065](https://github.com/mapbox/mapbox-gl-native/pull/15065)) * `MGLLoggingLevel` has been updated for better matching core log levels. Now can use `[MGLLoggingConfiguration sharedConfiguration].loggingLevel` to filter logs from core . [#15120](https://github.com/mapbox/mapbox-gl-native/pull/15120) * Fixed an issue where animated camera transitions zoomed in or out too dramatically. [#15281](https://github.com/mapbox/mapbox-gl-native/pull/15281) +* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)) ### Styles and rendering diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md index 8271e5fcfba..fa798d26768 100644 --- a/platform/node/CHANGELOG.md +++ b/platform/node/CHANGELOG.md @@ -1,5 +1,6 @@ # master * Introduce `text-writing-mode` layout property for symbol layer ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)). The `text-writing-mode` layout property allows control over symbol's preferred writing mode. The new property value is an array, whose values are enumeration values from a ( `horizontal` | `vertical` ) set. +* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)). # 4.2.0 - Add an option to set whether or not an image should be treated as a SDF ([#15054](https://github.com/mapbox/mapbox-gl-native/issues/15054))