summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/layout/symbol_instance.cpp68
-rw-r--r--src/mbgl/layout/symbol_instance.hpp27
-rw-r--r--src/mbgl/layout/symbol_layout.cpp203
-rw-r--r--src/mbgl/layout/symbol_layout.hpp7
-rw-r--r--src/mbgl/layout/symbol_projection.cpp6
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp20
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp6
-rw-r--r--src/mbgl/text/glyph.hpp8
-rw-r--r--src/mbgl/text/placement.cpp11
-rw-r--r--src/mbgl/text/placement.hpp12
-rw-r--r--src/mbgl/text/quads.cpp6
-rw-r--r--src/mbgl/text/quads.hpp1
-rw-r--r--src/mbgl/text/shaping.cpp71
-rw-r--r--src/mbgl/text/shaping.hpp15
14 files changed, 345 insertions, 116 deletions
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 139a42113c..fddaaf7c2d 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -5,13 +5,25 @@ namespace mbgl {
using namespace style;
+namespace {
+
+const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientations) {
+ if (shapedTextOrientations.right) return shapedTextOrientations.right;
+ if (shapedTextOrientations.center) return shapedTextOrientations.center;
+ if (shapedTextOrientations.left) return shapedTextOrientations.left;
+ if (shapedTextOrientations.vertical) return shapedTextOrientations.vertical;
+ return shapedTextOrientations.horizontal;
+}
+
+} // namespace
+
SymbolInstance::SymbolInstance(Anchor& anchor_,
GeometryCoordinates line_,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const ShapedTextOrientations& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const float textBoxScale,
+ const float textBoxScale_,
const float textPadding,
const SymbolPlacementType textPlacement,
const std::array<float, 2> textOffset_,
@@ -24,43 +36,59 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
const std::size_t dataFeatureIndex_,
const std::u16string& key_,
const float overscaling,
- const float rotate) :
+ const float rotate,
+ float radialTextOffset_) :
anchor(anchor_),
line(line_),
hasText(false),
hasIcon(shapedIcon),
// Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling, rotate),
+ // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature
+ textCollisionFeature(line_, anchor, getAnyShaping(shapedTextOrientations), textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, rotate),
iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, rotate),
+ writingModes(WritingModeType::None),
layoutFeatureIndex(layoutFeatureIndex_),
dataFeatureIndex(dataFeatureIndex_),
textOffset(textOffset_),
iconOffset(iconOffset_),
- key(key_) {
+ key(key_),
+ textBoxScale(textBoxScale_),
+ radialTextOffset(radialTextOffset_) {
// Create the quads used for rendering the icon and glyphs.
if (shapedIcon) {
- iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.horizontal);
+ }
+
+ if (shapedTextOrientations.right) {
+ writingModes |= WritingModeType::Horizontal;
+ rightJustifiedGlyphQuads = getGlyphQuads(shapedTextOrientations.right, textOffset, layout, textPlacement, positions);
}
- if (shapedTextOrientations.first) {
- horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
+
+ if (shapedTextOrientations.center) {
+ writingModes |= WritingModeType::Horizontal;
+ centerJustifiedGlyphQuads = getGlyphQuads(shapedTextOrientations.center, textOffset, layout, textPlacement, positions);
}
- if (shapedTextOrientations.second) {
- verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
+
+ if (shapedTextOrientations.left) {
+ writingModes |= WritingModeType::Horizontal;
+ leftJustifiedGlyphQuads = getGlyphQuads(shapedTextOrientations.left, textOffset, layout, textPlacement, positions);
}
- // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
- hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0;
- if (shapedTextOrientations.first && shapedTextOrientations.second) {
- writingModes = WritingModeType::Horizontal | WritingModeType::Vertical;
- } else if (shapedTextOrientations.first) {
- writingModes = WritingModeType::Horizontal;
- } else if (shapedTextOrientations.second) {
- writingModes = WritingModeType::Vertical;
- } else {
- writingModes = WritingModeType::None;
+ if (shapedTextOrientations.vertical) {
+ writingModes |= WritingModeType::Vertical;
+ verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.vertical, textOffset, layout, textPlacement, positions);
}
+
+ // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
+ hasText = !rightJustifiedGlyphQuads.empty() || !centerJustifiedGlyphQuads.empty() || !leftJustifiedGlyphQuads.empty() || !verticalGlyphQuads.empty();
}
+optional<size_t> SymbolInstance::getDefaultHorizontalPlacedTextIndex() const {
+ if (placedRightTextIndex) return placedRightTextIndex;
+ if (placedCenterTextIndex) return placedCenterTextIndex;
+ if (placedLeftTextIndex) return placedLeftTextIndex;
+ return nullopt;
+}
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index 6148d7fe88..44d81ae1e5 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -11,11 +11,20 @@ namespace mbgl {
class Anchor;
class IndexedSubfeature;
+struct ShapedTextOrientations {
+ Shaping horizontal;
+ Shaping vertical;
+ // The following are used with variable text placement on.
+ Shaping& right = horizontal;
+ Shaping center;
+ Shaping left;
+};
+
class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
GeometryCoordinates line,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const ShapedTextOrientations& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
@@ -32,14 +41,20 @@ public:
const std::size_t dataFeatureIndex,
const std::u16string& key,
const float overscaling,
- const float rotate);
+ const float rotate,
+ float radialTextOffset);
+
+ optional<size_t> getDefaultHorizontalPlacedTextIndex() const;
Anchor anchor;
GeometryCoordinates line;
bool hasText;
bool hasIcon;
- SymbolQuads horizontalGlyphQuads;
+ SymbolQuads rightJustifiedGlyphQuads;
+ SymbolQuads centerJustifiedGlyphQuads;
+ SymbolQuads leftJustifiedGlyphQuads;
SymbolQuads verticalGlyphQuads;
+
optional<SymbolQuad> iconQuad;
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
@@ -50,9 +65,13 @@ public:
std::array<float, 2> iconOffset;
std::u16string key;
bool isDuplicate;
- optional<size_t> placedTextIndex;
+ optional<size_t> placedRightTextIndex;
+ optional<size_t> placedCenterTextIndex;
+ optional<size_t> placedLeftTextIndex;
optional<size_t> placedVerticalTextIndex;
optional<size_t> placedIconIndex;
+ float textBoxScale;
+ float radialTextOffset;
uint32_t crossTileID = 0;
};
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index c40a705d7f..52ec2aa843 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -174,44 +174,171 @@ bool SymbolLayout::hasSymbolInstances() const {
return !symbolInstances.empty();
}
+namespace {
+
+// The radial offset is to the edge of the text box
+// In the horizontal direction, the edge of the text box is where glyphs start
+// But in the vertical direction, the glyphs appear to "start" at the baseline
+// We don't actually load baseline data, but we assume an offset of ONE_EM - 17
+// (see "yOffset" in shaping.js)
+const float baselineOffset = 7.0f;
+
+// We don't care which shaping we get because this is used for collision purposes
+// and all the justifications have the same collision box.
+const Shaping& getDefaultHorizontalShaping(const ShapedTextOrientations& shapedTextOrientations) {
+ if (shapedTextOrientations.right) return shapedTextOrientations.right;
+ if (shapedTextOrientations.center) return shapedTextOrientations.center;
+ if (shapedTextOrientations.left) return shapedTextOrientations.left;
+ return shapedTextOrientations.horizontal;
+}
+
+Shaping& shapingForTextJustifyType(ShapedTextOrientations& shapedTextOrientations, style::TextJustifyType type) {
+ switch(type) {
+ case style::TextJustifyType::Right: return shapedTextOrientations.right;
+ case style::TextJustifyType::Left: return shapedTextOrientations.left;
+ case style::TextJustifyType::Center: return shapedTextOrientations.center;
+ default:
+ assert(false);
+ return shapedTextOrientations.horizontal;
+ }
+}
+
+} // namespace
+
+// static
+Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float radialOffset) {
+ Point<float> result{};
+ // solve for r where r^2 + r^2 = radialOffset^2
+ const float sqrt2 = 1.41421356237f;
+ const float hypotenuse = radialOffset / sqrt2;
+
+ switch (anchor) {
+ case SymbolAnchorType::TopRight:
+ case SymbolAnchorType::TopLeft:
+ result.y = hypotenuse - baselineOffset;
+ break;
+ case SymbolAnchorType::BottomRight:
+ case SymbolAnchorType::BottomLeft:
+ result.y = -hypotenuse + baselineOffset;
+ break;
+ case SymbolAnchorType::Bottom:
+ result.y = -radialOffset + baselineOffset;
+ break;
+ case SymbolAnchorType::Top:
+ result.y = radialOffset - baselineOffset;
+ break;
+ default:
+ break;
+ }
+
+ switch (anchor) {
+ case SymbolAnchorType::TopRight:
+ case SymbolAnchorType::BottomRight:
+ result.x = -hypotenuse;
+ break;
+ case SymbolAnchorType::TopLeft:
+ case SymbolAnchorType::BottomLeft:
+ result.x = hypotenuse;
+ break;
+ case SymbolAnchorType::Left:
+ result.x = radialOffset;
+ break;
+ case SymbolAnchorType::Right:
+ result.x = -radialOffset;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
const ImageMap& imageMap, const ImagePositions& imagePositions) {
- const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
- layout.get<SymbolPlacement>() != SymbolPlacementType::Point;
+ const bool isPointPlacement = layout.get<SymbolPlacement>() == SymbolPlacementType::Point;
+ const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && !isPointPlacement;
for (auto it = features.begin(); it != features.end(); ++it) {
auto& feature = *it;
if (feature.geometry.empty()) continue;
- std::pair<Shaping, Shaping> shapedTextOrientations;
+ ShapedTextOrientations shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
+ Point<float> textOffset;
// if feature has text, shape the text
if (feature.formattedText) {
- auto applyShaping = [&] (const TaggedString& formattedText, WritingModeType writingMode) {
- const float oneEm = 24.0f;
+ const float lineHeight = layout.get<TextLineHeight>() * util::ONE_EM;
+ const float spacing = util::i18n::allowsLetterSpacing(feature.formattedText->rawText()) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * util::ONE_EM : 0.0f;
+
+ auto applyShaping = [&] (const TaggedString& formattedText, WritingModeType writingMode, SymbolAnchorType textAnchor, TextJustifyType textJustify) {
const Shaping result = getShaping(
/* string */ formattedText,
- /* maxWidth: ems */ layout.get<SymbolPlacement>() == SymbolPlacementType::Point ?
- layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
- /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
- /* anchor */ layout.evaluate<TextAnchor>(zoom, feature),
- /* justify */ layout.evaluate<TextJustify>(zoom, feature),
- /* spacing: ems */ util::i18n::allowsLetterSpacing(feature.formattedText->rawText()) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f,
- /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
- /* verticalHeight */ oneEm,
+ /* maxWidth: ems */ isPointPlacement ? layout.evaluate<TextMaxWidth>(zoom, feature) * util::ONE_EM : 0.0f,
+ /* ems */ lineHeight,
+ textAnchor,
+ textJustify,
+ /* ems */ spacing,
+ /* translate */ textOffset,
/* writingMode */ writingMode,
/* bidirectional algorithm object */ bidi,
/* glyphs */ glyphMap);
return result;
};
+ const std::vector<style::TextVariableAnchorType> variableTextAnchor = layout.evaluate<TextVariableAnchor>(zoom, feature);
+ const float radialOffset = layout.evaluate<TextRadialOffset>(zoom, feature);
+ const SymbolAnchorType textAnchor = layout.evaluate<TextAnchor>(zoom, feature);
+ if (variableTextAnchor.empty()) {
+ // Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
+ // is calculated at placement time instead of layout time
+ if (radialOffset > 0.0f) {
+ // The style spec says don't use `text-offset` and `text-radial-offset` together
+ // but doesn't actually specify what happens if you use both. We go with the radial offset.
+ textOffset = evaluateRadialOffset(textAnchor, radialOffset * util::ONE_EM);
+ } else {
+ textOffset = { layout.evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
+ layout.evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM};
+ }
+ }
+ TextJustifyType textJustify = textAlongLine ? TextJustifyType::Center : layout.evaluate<TextJustify>(zoom, feature);
+ // If this layer uses text-variable-anchor, generate shapings for all justification possibilities.
+ if (!textAlongLine && !variableTextAnchor.empty()) {
+ std::vector<TextJustifyType> justifications;
+ if (textJustify != TextJustifyType::Auto) {
+ justifications.push_back(textJustify);
+ } else {
+ for (auto anchor : variableTextAnchor) {
+ justifications.push_back(getAnchorJustification(anchor));
+ }
+ }
- shapedTextOrientations.first = applyShaping(*feature.formattedText, WritingModeType::Horizontal);
+ for (TextJustifyType justification: justifications) {
+ Shaping& shapingForJustification = shapingForTextJustifyType(shapedTextOrientations, justification);
+ if (shapingForJustification) {
+ continue;
+ }
+ // If using text-variable-anchor for the layer, we use a center anchor for all shapings and apply
+ // the offsets for the anchor in the placement step.
+ Shaping shaping = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::Center, justification);
+ if (shaping) {
+ shapingForJustification = std::move(shaping);
+ }
+ // TODO: use 'singleLine' optimization.
+ }
+ } else {
+ if (textJustify == TextJustifyType::Auto) {
+ textJustify = getAnchorJustification(textAnchor);
+ }
+ Shaping shaping = applyShaping(*feature.formattedText, WritingModeType::Horizontal, textAnchor, textJustify);
+ if (shaping) {
+ shapedTextOrientations.horizontal = std::move(shaping);
+ }
- if (util::i18n::allowsVerticalWritingMode(feature.formattedText->rawText()) && textAlongLine) {
- feature.formattedText->verticalizePunctuation();
- shapedTextOrientations.second = applyShaping(*feature.formattedText, WritingModeType::Vertical);
+ if (util::i18n::allowsVerticalWritingMode(feature.formattedText->rawText()) && textAlongLine) {
+ feature.formattedText->verticalizePunctuation();
+ shapedTextOrientations.vertical = applyShaping(*feature.formattedText, WritingModeType::Vertical, textAnchor, textJustify);
+ }
}
}
@@ -236,8 +363,8 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
}
// if either shapedText or icon position is present, add the feature
- if (shapedTextOrientations.first || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions);
+ if (getDefaultHorizontalShaping(shapedTextOrientations) || shapedIcon) {
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions, textOffset);
}
feature.geometry.clear();
@@ -248,15 +375,17 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const SymbolFeature& feature,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const ShapedTextOrientations& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositions& glyphPositions) {
+ const GlyphPositions& glyphPositions,
+ Point<float> offset) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
- const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
+ const std::array<float, 2> textOffset = {{ offset.x, offset.y }};
+
const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);
// To reduce the number of labels that jump around when zooming we need
@@ -274,6 +403,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float iconPadding = layout.get<IconPadding>() * tilePixelRatio;
const float textMaxAngle = layout.get<TextMaxAngle>() * util::DEG2RAD;
const float rotation = layout.evaluate<IconRotate>(zoom, feature);
+ const float radialTextOffset = layout.evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM;
const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
@@ -296,7 +426,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
textBoxScale, textPadding, textPlacement, textOffset,
iconBoxScale, iconPadding, iconOffset,
glyphPositions, indexedFeature, layoutFeatureIndex, feature.index,
- feature.formattedText ? feature.formattedText->rawText() : std::u16string(), overscaling, rotation);
+ feature.formattedText ? feature.formattedText->rawText() : std::u16string(), overscaling, rotation, radialTextOffset);
}
};
@@ -308,8 +438,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
Anchors anchors = getAnchors(line,
symbolSpacing,
textMaxAngle,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).left,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).right,
+ (shapedTextOrientations.vertical ?: getDefaultHorizontalShaping(shapedTextOrientations)).left,
+ (shapedTextOrientations.vertical ?: getDefaultHorizontalShaping(shapedTextOrientations)).right,
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
glyphSize,
@@ -329,8 +459,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
if (line.size() > 1) {
optional<Anchor> anchor = getCenterAnchor(line,
textMaxAngle,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).left,
- (shapedTextOrientations.second ?: shapedTextOrientations.first).right,
+ (shapedTextOrientations.vertical ?: getDefaultHorizontalShaping(shapedTextOrientations)).left,
+ (shapedTextOrientations.vertical ?: getDefaultHorizontalShaping(shapedTextOrientations)).right,
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
glyphSize,
@@ -414,7 +544,7 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
const bool sortFeaturesByY = zOrderByViewport && (layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>());
- auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances));
+ auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances), tilePixelRatio);
for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
@@ -426,13 +556,22 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
// Insert final placement into collision tree and add glyphs/icons to buffers
if (hasText && feature.formattedText) {
- std::size_t index = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedTextIndex, symbolInstance.horizontalGlyphQuads);
+ optional<std::size_t> lastAddedSection;
+ if (!symbolInstance.rightJustifiedGlyphQuads.empty()) {
+ lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedRightTextIndex, symbolInstance.rightJustifiedGlyphQuads, lastAddedSection);
+ }
+ if (!symbolInstance.centerJustifiedGlyphQuads.empty()) {
+ lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedCenterTextIndex, symbolInstance.centerJustifiedGlyphQuads, lastAddedSection);
+ }
+ if (!symbolInstance.leftJustifiedGlyphQuads.empty()) {
+ lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedLeftTextIndex, symbolInstance.leftJustifiedGlyphQuads, lastAddedSection);
+ }
if (symbolInstance.writingModes & WritingModeType::Vertical) {
- index = addSymbolGlyphQuads(*bucket, symbolInstance, feature, WritingModeType::Vertical, symbolInstance.placedVerticalTextIndex, symbolInstance.verticalGlyphQuads, index);
+ lastAddedSection = addSymbolGlyphQuads(*bucket, symbolInstance, feature, WritingModeType::Vertical, symbolInstance.placedVerticalTextIndex, symbolInstance.verticalGlyphQuads, lastAddedSection);
}
-
- updatePaintPropertiesForSection(*bucket, feature, index);
+ assert(lastAddedSection); // True, as hasText == true;
+ updatePaintPropertiesForSection(*bucket, feature, *lastAddedSection);
}
if (hasIcon) {
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 53c66d31fe..d88c79c552 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -46,12 +46,15 @@ public:
const std::string bucketLeaderID;
std::vector<SymbolInstance> symbolInstances;
+ static Point<float> evaluateRadialOffset(style::SymbolAnchorType anchor, float radialOffset);
+
private:
void addFeature(const size_t,
const SymbolFeature&,
- const std::pair<Shaping, Shaping>& shapedTextOrientations,
+ const ShapedTextOrientations& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositions&);
+ const GlyphPositions&,
+ Point<float> textOffset);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
index dff2a569ac..b7858f8deb 100644
--- a/src/mbgl/layout/symbol_projection.cpp
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -291,9 +291,9 @@ namespace mbgl {
gfx::VertexVector<gfx::Vertex<SymbolDynamicLayoutAttributes>>& dynamicVertexArray,
const Point<float>& projectedAnchorPoint,
const float aspectRatio) {
- const float fontScale = fontSize / 24.0;
- const float lineOffsetX = symbol.lineOffset[0] * fontSize;
- const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+ const float fontScale = fontSize / util::ONE_EM;
+ const float lineOffsetX = symbol.lineOffset[0] * fontScale;
+ const float lineOffsetY = symbol.lineOffset[1] * fontScale;
std::vector<PlacedGlyph> placedGlyphs;
if (symbol.glyphOffsets.size() > 1) {
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index 9220235f1d..38342b44ee 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -17,7 +17,8 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
bool iconsNeedLinear_,
bool sortFeaturesByY_,
const std::string bucketName_,
- const std::vector<SymbolInstance>&& symbolInstances_)
+ const std::vector<SymbolInstance>&& symbolInstances_,
+ float tilePixelRatio_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
@@ -25,7 +26,8 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
bucketLeaderID(std::move(bucketName_)),
symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
- iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
+ iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())),
+ tilePixelRatio(tilePixelRatio_) {
for (const auto& pair : paintProperties_) {
auto layerPaintProperties = pair.second;
@@ -218,12 +220,22 @@ void SymbolBucket::sortFeatures(const float angle) {
const SymbolInstance& symbolInstance = symbolInstances[i];
featureSortOrder->push_back(symbolInstance.dataFeatureIndex);
- if (symbolInstance.placedTextIndex) {
- addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
+ if (symbolInstance.placedRightTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedRightTextIndex]);
}
+
+ if (symbolInstance.placedCenterTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedCenterTextIndex]);
+ }
+
+ if (symbolInstance.placedLeftTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedLeftTextIndex]);
+ }
+
if (symbolInstance.placedVerticalTextIndex) {
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
}
+
if (symbolInstance.placedIconIndex) {
addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
}
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index fafa2592fe..f8ffc1a8eb 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -35,6 +35,8 @@ public:
std::vector<float> glyphOffsets;
bool hidden;
size_t vertexStartIndex;
+ // The crossTileID is only filled/used on the foreground for variable text anchors
+ uint32_t crossTileID = 0u;
};
class SymbolBucket final : public Bucket {
@@ -48,7 +50,8 @@ public:
bool iconsNeedLinear,
bool sortFeaturesByY,
const std::string bucketLeaderID,
- const std::vector<SymbolInstance>&&);
+ const std::vector<SymbolInstance>&&,
+ const float tilePixelRatio);
~SymbolBucket() override;
void upload(gfx::Context&) override;
@@ -130,6 +133,7 @@ public:
optional<gfx::IndexBuffer> indexBuffer;
} collisionCircle;
+ const float tilePixelRatio;
uint32_t bucketInstanceId = 0;
bool justReloaded = false;
optional<bool> hasFormatSectionOverrides_;
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index c97b242c10..7d6415c057 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -78,15 +78,17 @@ enum class WritingModeType : uint8_t;
class Shaping {
public:
- explicit Shaping() = default;
- explicit Shaping(float x, float y, WritingModeType writingMode_)
- : top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {}
+ Shaping() = default;
+ explicit Shaping(float x, float y, WritingModeType writingMode_, std::size_t lineCount_)
+ : top(y), bottom(y), left(x), right(x), writingMode(writingMode_), lineCount(lineCount_) {}
std::vector<PositionedGlyph> positionedGlyphs;
float top = 0;
float bottom = 0;
float left = 0;
float right = 0;
WritingModeType writingMode;
+ std::size_t lineCount = 0u;
+ std::string text = {};
explicit operator bool() const { return !positionedGlyphs.empty(); }
};
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index 4cc12b0980..bb7e2d1a6c 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -176,8 +176,8 @@ void Placement::placeLayerBucket(
bool placeIcon = false;
bool offscreen = true;
- if (symbolInstance.placedTextIndex) {
- PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
+ if (symbolInstance.placedRightTextIndex) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
@@ -339,14 +339,15 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
if (symbolInstance.hasText) {
auto opacityVertex = SymbolSDFTextProgram::opacityVertex(opacityState.text.placed, opacityState.text.opacity);
- for (size_t i = 0; i < symbolInstance.horizontalGlyphQuads.size() * 4; i++) {
+ for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuads.size() * 4; i++) {
bucket.text.opacityVertices.emplace_back(opacityVertex);
}
for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
bucket.text.opacityVertices.emplace_back(opacityVertex);
}
- if (symbolInstance.placedTextIndex) {
- bucket.text.placedSymbols[*symbolInstance.placedTextIndex].hidden = opacityState.isHidden();
+ if (symbolInstance.placedRightTextIndex) {
+ PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedRightTextIndex];
+ placed.hidden = opacityState.isHidden();
}
if (symbolInstance.placedVerticalTextIndex) {
bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden();
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
index cc23110e54..32310f723e 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -31,6 +31,16 @@ public:
OpacityState text;
};
+class VariableOffset {
+public:
+ float radialOffset;
+ float width;
+ float height;
+ style::TextVariableAnchorType anchor;
+ float textBoxScale;
+ optional<style::TextVariableAnchorType> prevAnchor;
+};
+
class JointPlacement {
public:
JointPlacement(bool text_, bool icon_, bool skipFade_)
@@ -45,7 +55,7 @@ public:
// visible right away.
const bool skipFade;
};
-
+
struct RetainedQueryData {
uint32_t bucketInstanceId;
std::shared_ptr<FeatureIndex> featureIndex;
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index ec0045caad..6be5d8c01e 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -92,16 +92,12 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
}
SymbolQuads getGlyphQuads(const Shaping& shapedText,
+ const std::array<float, 2> textOffset,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
const GlyphPositions& positions) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
- const float oneEm = 24.0;
- std::array<float, 2> textOffset = layout.get<TextOffset>();
- textOffset[0] *= oneEm;
- textOffset[1] *= oneEm;
-
SymbolQuads quads;
for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) {
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index f41a4fec66..0bb892e4d1 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -49,6 +49,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const Shaping& shapedText);
SymbolQuads getGlyphQuads(const Shaping& shapedText,
+ const std::array<float, 2> textOffset,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
const GlyphPositions& positions);
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 02dbf146e1..348c2ddccc 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -1,4 +1,5 @@
#include <mbgl/text/shaping.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/i18n.hpp>
#include <mbgl/layout/symbol_feature.hpp>
#include <mbgl/math/minmax.hpp>
@@ -10,58 +11,61 @@
namespace mbgl {
-struct AnchorAlignment {
- AnchorAlignment(float horizontal_, float vertical_)
- : horizontalAlign(horizontal_), verticalAlign(vertical_) {
- }
-
- float horizontalAlign;
- float verticalAlign;
-};
-AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
+// static
+AnchorAlignment AnchorAlignment::getAnchorAlignment(style::SymbolAnchorType anchor) {
+ AnchorAlignment result(0.5f, 0.5f);
switch (anchor) {
- case style::SymbolAnchorType::Top:
- case style::SymbolAnchorType::Bottom:
- case style::SymbolAnchorType::Center:
- break;
case style::SymbolAnchorType::Right:
case style::SymbolAnchorType::TopRight:
case style::SymbolAnchorType::BottomRight:
- horizontalAlign = 1;
+ result.horizontalAlign = 1.0f;
break;
case style::SymbolAnchorType::Left:
case style::SymbolAnchorType::TopLeft:
case style::SymbolAnchorType::BottomLeft:
- horizontalAlign = 0;
+ result.horizontalAlign = 0.0f;
break;
+ default:
+ break;
}
switch (anchor) {
- case style::SymbolAnchorType::Left:
- case style::SymbolAnchorType::Right:
- case style::SymbolAnchorType::Center:
- break;
case style::SymbolAnchorType::Bottom:
case style::SymbolAnchorType::BottomLeft:
case style::SymbolAnchorType::BottomRight:
- verticalAlign = 1;
+ result.verticalAlign = 1.0f;
break;
case style::SymbolAnchorType::Top:
case style::SymbolAnchorType::TopLeft:
case style::SymbolAnchorType::TopRight:
- verticalAlign = 0;
+ result.verticalAlign = 0.0f;
+ break;
+ default:
break;
}
- return AnchorAlignment(horizontalAlign, verticalAlign);
+ return result;
+}
+
+style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor) {
+ switch (anchor) {
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::TopRight:
+ case style::SymbolAnchorType::BottomRight:
+ return style::TextJustifyType::Right;
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::BottomLeft:
+ return style::TextJustifyType::Left;
+ default:
+ return style::TextJustifyType::Center;
+ }
}
PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
- AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor);
+ AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
@@ -269,7 +273,6 @@ void shapeLines(Shaping& shaping,
const float lineHeight,
const style::SymbolAnchorType textAnchor,
const style::TextJustifyType textJustify,
- const float verticalHeight,
const WritingModeType writingMode,
const GlyphMap& glyphMap) {
@@ -314,7 +317,7 @@ void shapeLines(Shaping& shaping,
// We don't know the baseline, but since we're laying out
// at 24 points, we can calculate how much it will move when
// we scale up or down.
- const double baselineOffset = (lineMaxScale - section.scale) * 24;
+ const double baselineOffset = (lineMaxScale - section.scale) * util::ONE_EM;
const Glyph& glyph = **it->second;
@@ -323,7 +326,7 @@ void shapeLines(Shaping& shaping,
x += glyph.metrics.advance * section.scale + spacing;
} else {
shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
- x += verticalHeight * section.scale + spacing;
+ x += util::ONE_EM * section.scale + spacing;
}
}
@@ -340,7 +343,7 @@ void shapeLines(Shaping& shaping,
y += lineHeight * lineMaxScale;
}
- auto anchorAlign = getAnchorAlignment(textAnchor);
+ auto anchorAlign = AnchorAlignment::getAnchorAlignment(textAnchor);
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
lineHeight, lines.size());
@@ -360,12 +363,10 @@ const Shaping getShaping(const TaggedString& formattedString,
const style::TextJustifyType textJustify,
const float spacing,
const Point<float>& translate,
- const float verticalHeight,
+ //const float verticalHeight,
const WritingModeType writingMode,
BiDi& bidi,
- const GlyphMap& glyphs) {
- Shaping shaping(translate.x, translate.y, writingMode);
-
+ const GlyphMap& glyphs) {
std::vector<TaggedString> reorderedLines;
if (formattedString.sectionCount() == 1) {
auto untaggedLines = bidi.processText(formattedString.rawText(),
@@ -380,9 +381,9 @@ const Shaping getShaping(const TaggedString& formattedString,
reorderedLines.emplace_back(line, formattedString.getSections());
}
}
-
+ Shaping shaping(translate.x, translate.y, writingMode, reorderedLines.size());
shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor,
- textJustify, verticalHeight, writingMode, glyphs);
+ textJustify, writingMode, glyphs);
return shaping;
}
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 50ac893098..766b1ce233 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -7,6 +7,20 @@
namespace mbgl {
+struct AnchorAlignment {
+ AnchorAlignment(float horizontal, float vertical)
+ : horizontalAlign(horizontal), verticalAlign(vertical) {
+ }
+
+ static AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor);
+
+ float horizontalAlign;
+ float verticalAlign;
+};
+
+// Choose the justification that matches the direction of the TextAnchor
+style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor);
+
class SymbolFeature;
class BiDi;
@@ -53,7 +67,6 @@ const Shaping getShaping(const TaggedString& string,
style::TextJustifyType textJustify,
float spacing,
const Point<float>& translate,
- float verticalHeight,
const WritingModeType,
BiDi& bidi,
const GlyphMap& glyphs);