[core] Fix combination of icon-text-fit with text-variable-anchors and text-writing-mode
This commit is contained in:
@@ -21,15 +21,18 @@ const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientation
|
||||
SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& verticallyShapedIcon,
|
||||
const style::SymbolLayoutProperties::Evaluated& layout,
|
||||
const float layoutTextSize,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
const std::array<float, 2>& 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<SymbolInstanceSharedData> sharedData_,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& 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<SymbolQuad>& SymbolInstance::iconQuad() const {
|
||||
return sharedData->iconQuad;
|
||||
}
|
||||
|
||||
const optional<SymbolQuad>& SymbolInstance::verticalIconQuad() const {
|
||||
assert(sharedData);
|
||||
return sharedData->verticalIconQuad;
|
||||
}
|
||||
|
||||
void SymbolInstance::releaseSharedData() {
|
||||
sharedData.reset();
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ struct SymbolInstanceSharedData {
|
||||
SymbolInstanceSharedData(GeometryCoordinates line,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& verticallyShapedIcon,
|
||||
const style::SymbolLayoutProperties::Evaluated& layout,
|
||||
const float layoutTextSize,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
const std::array<float, 2>& textOffset,
|
||||
const GlyphPositions& positions,
|
||||
@@ -39,6 +39,7 @@ struct SymbolInstanceSharedData {
|
||||
SymbolQuads leftJustifiedGlyphQuads;
|
||||
SymbolQuads verticalGlyphQuads;
|
||||
optional<SymbolQuad> iconQuad;
|
||||
optional<SymbolQuad> verticalIconQuad;
|
||||
};
|
||||
|
||||
class SymbolInstance {
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
std::shared_ptr<SymbolInstanceSharedData> sharedData,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& verticallyShapedIcon,
|
||||
const float textBoxScale,
|
||||
const float textPadding,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
const SymbolQuads& centerJustifiedGlyphQuads() const;
|
||||
const SymbolQuads& verticalGlyphQuads() const;
|
||||
const optional<SymbolQuad>& iconQuad() const;
|
||||
const optional<SymbolQuad>& verticalIconQuad() const;
|
||||
void releaseSharedData();
|
||||
|
||||
private:
|
||||
@@ -89,6 +92,7 @@ public:
|
||||
CollisionFeature textCollisionFeature;
|
||||
CollisionFeature iconCollisionFeature;
|
||||
optional<CollisionFeature> verticalTextCollisionFeature = nullopt;
|
||||
optional<CollisionFeature> 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 @@ public:
|
||||
optional<size_t> placedLeftTextIndex;
|
||||
optional<size_t> placedVerticalTextIndex;
|
||||
optional<size_t> placedIconIndex;
|
||||
optional<size_t> placedVerticalIconIndex;
|
||||
float textBoxScale;
|
||||
float radialTextOffset;
|
||||
bool singleLine;
|
||||
|
||||
@@ -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<PositionedIcon>& shapedIcon,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositions& glyphPositions,
|
||||
Point<float> 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<style::IconTextFit>() != IconTextFitType::None;
|
||||
|
||||
// Adjust shaped icon size when icon-text-fit is used.
|
||||
optional<PositionedIcon> 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<SymbolInstanceSharedData> 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<SymbolInstanceSharedData>(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<FeatureIn
|
||||
|
||||
// Insert final placement into collision tree and add glyphs/icons to buffers
|
||||
|
||||
// Process icon first, so that text symbols would have reference to iconIndex which
|
||||
// is used when dynamic vertices for icon-text-fit image have to be updated.
|
||||
if (hasIcon) {
|
||||
if (symbolInstance.hasIcon) {
|
||||
const Range<float> 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<float>());
|
||||
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<std::size_t> lastAddedSection;
|
||||
if (singleLine) {
|
||||
@@ -643,21 +686,6 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
|
||||
updatePaintPropertiesForSection(*bucket, feature, *lastAddedSection);
|
||||
}
|
||||
|
||||
if (hasIcon) {
|
||||
if (symbolInstance.hasIcon) {
|
||||
const Range<float> 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<float>());
|
||||
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<std::size_t> lastAddedSection) {
|
||||
const Range<float> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
void addFeature(const size_t,
|
||||
const SymbolFeature&,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositions&,
|
||||
Point<float> textOffset);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ class CrossTileSymbolLayerIndex;
|
||||
class PlacedSymbol {
|
||||
public:
|
||||
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
|
||||
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
|
||||
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_, optional<size_t> 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<float> anchorPoint;
|
||||
@@ -43,6 +43,9 @@ public:
|
||||
// placement for orientation variants.
|
||||
optional<style::TextWritingModeType> placedOrientation;
|
||||
float angle = 0;
|
||||
|
||||
// Reference to placed icon, only applicable for text symbols.
|
||||
optional<size_t> placedIconIndex;
|
||||
};
|
||||
|
||||
class SymbolBucket final : public Bucket {
|
||||
|
||||
@@ -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<IconTextFit>() != 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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -16,9 +16,9 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
|
||||
style::SymbolPlacementType placementType = style::SymbolPlacementType::Point;
|
||||
|
||||
auto sharedData = std::make_shared<SymbolInstanceSharedData>(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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<TextSize>() = 24.0f;
|
||||
layout.get<IconTextFit>() = 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<TextSize>() = 12.0f;
|
||||
layout.get<IconTextFit>() = 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<IconTextFitPadding>()[1] = 10.0f;
|
||||
layout.get<IconTextFitPadding>()[2] = 5.0f;
|
||||
layout.get<IconTextFitPadding>()[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<TextSize>() = 24.0f;
|
||||
layout.get<IconTextFit>() = 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<TextSize>() = 12.0f;
|
||||
layout.get<IconTextFit>() = 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<IconTextFitPadding>()[1] = 10.0f;
|
||||
layout.get<IconTextFitPadding>()[2] = 5.0f;
|
||||
layout.get<IconTextFitPadding>()[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<TextSize>() = 24.0f;
|
||||
layout.get<IconTextFit>() = 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);
|
||||
getIconQuad(shapedIcon, WritingModeType::Horizontal);
|
||||
|
||||
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);
|
||||
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<TextSize>() = 12.0f;
|
||||
layout.get<IconTextFit>() = 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);
|
||||
getIconQuad(shapedIcon, WritingModeType::Horizontal);
|
||||
|
||||
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);
|
||||
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<IconTextFitPadding>()[1] = 10.0f;
|
||||
layout.get<IconTextFitPadding>()[2] = 5.0f;
|
||||
layout.get<IconTextFitPadding>()[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.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);
|
||||
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<IconTextFitPadding>()[1] = 5.0f;
|
||||
layout.get<IconTextFitPadding>()[2] = 10.0f;
|
||||
layout.get<IconTextFitPadding>()[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);
|
||||
getIconQuad(shapedIcon, WritingModeType::Horizontal);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user