From 8be135231d9efe41a3b12037518d02b36104e8cf Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Mon, 11 Mar 2019 10:26:19 +0200 Subject: [core] Add possibility of overriding paint properties inside format expression #14062 * [core] Add format override expression and formatted section to evaluation context * [core] Add textColor to TaggedString's formatted section * [core] Add FormatSectionOverrides and introduce overridable properties * [core] Populate symbol layer paint properties for text sections * [core] Add benchmark for style that uses text-color override * [core] Add unit test for FormatOverrideExpression * [core] Add unit test for FormatSectionOverrides --- src/core-files.json | 2 + src/mbgl/layout/symbol_layout.cpp | 138 ++++++++++++---------- src/mbgl/layout/symbol_layout.hpp | 32 +++-- src/mbgl/renderer/buckets/symbol_bucket.cpp | 29 ++++- src/mbgl/renderer/buckets/symbol_bucket.hpp | 20 ++-- src/mbgl/renderer/layers/render_symbol_layer.cpp | 2 +- src/mbgl/renderer/paint_property_binder.hpp | 36 +++--- src/mbgl/style/conversion/function.cpp | 2 +- src/mbgl/style/expression/dsl.cpp | 6 +- src/mbgl/style/expression/format_expression.cpp | 62 ++++++++-- src/mbgl/style/expression/formatted.cpp | 36 +++--- src/mbgl/style/expression/is_constant.cpp | 10 +- src/mbgl/style/layers/layer_properties.hpp.ejs | 5 + src/mbgl/style/layers/symbol_layer_impl.cpp | 15 ++- src/mbgl/style/layers/symbol_layer_impl.hpp | 98 +++++++++++++++ src/mbgl/style/layers/symbol_layer_properties.hpp | 5 +- src/mbgl/style/layout_property.hpp | 2 + src/mbgl/style/light_impl.hpp | 1 + src/mbgl/style/paint_property.hpp | 7 +- src/mbgl/style/properties.hpp | 4 + src/mbgl/style/property_expression.cpp | 68 +++++++++++ src/mbgl/text/glyph.hpp | 8 +- src/mbgl/text/quads.cpp | 2 +- src/mbgl/text/quads.hpp | 7 +- src/mbgl/text/shaping.cpp | 7 +- src/mbgl/text/tagged_string.cpp | 7 +- src/mbgl/text/tagged_string.hpp | 20 +++- 27 files changed, 473 insertions(+), 158 deletions(-) create mode 100644 src/mbgl/style/property_expression.cpp (limited to 'src') diff --git a/src/core-files.json b/src/core-files.json index f745a409e5..8915ebc49d 100644 --- a/src/core-files.json +++ b/src/core-files.json @@ -225,6 +225,7 @@ "src/mbgl/style/light.cpp", "src/mbgl/style/light_impl.cpp", "src/mbgl/style/parser.cpp", + "src/mbgl/style/property_expression.cpp", "src/mbgl/style/source.cpp", "src/mbgl/style/source_impl.cpp", "src/mbgl/style/sources/custom_geometry_source.cpp", @@ -390,6 +391,7 @@ "mbgl/style/expression/expression.hpp": "include/mbgl/style/expression/expression.hpp", "mbgl/style/expression/find_zoom_curve.hpp": "include/mbgl/style/expression/find_zoom_curve.hpp", "mbgl/style/expression/format_expression.hpp": "include/mbgl/style/expression/format_expression.hpp", + "mbgl/style/expression/format_section_override.hpp": "include/mbgl/style/expression/format_section_override.hpp", "mbgl/style/expression/formatted.hpp": "include/mbgl/style/expression/formatted.hpp", "mbgl/style/expression/get_covering_stops.hpp": "include/mbgl/style/expression/get_covering_stops.hpp", "mbgl/style/expression/interpolate.hpp": "include/mbgl/style/expression/interpolate.hpp", diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 4041b16a65..c40a705d7f 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -1,25 +1,16 @@ -#include #include #include #include -#include #include #include #include -#include #include #include -#include #include -#include #include #include #include -#include -#include -#include #include -#include #include #include @@ -31,11 +22,27 @@ using namespace style; template static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout) { return layout.get().match( - [&] (const typename Property::Type& t) { return !t.empty(); }, - [&] (const auto&) { return true; } + [] (const typename Property::Type& t) { return !t.empty(); }, + [] (const auto&) { return true; } ); } +namespace { +expression::Value sectionOptionsToValue(const SectionOptions& options) { + std::unordered_map result; + // TODO: Data driven properties that can be overridden on per section basis. + // TextOpacity + // TextHaloColor + // TextHaloWidth + // TextHaloBlur + if (options.textColor) { + result.emplace(expression::kFormattedSectionTextColor, *options.textColor); + } + return result; +} +} // namespace + + SymbolLayout::SymbolLayout(const BucketParameters& parameters, const std::vector& layers, std::unique_ptr sourceLayer_, @@ -108,7 +115,6 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, auto formatted = layout.evaluate(zoom, ft); auto textTransform = layout.evaluate(zoom, ft); FontStack baseFontStack = layout.evaluate(zoom, ft); - FontStackHash baseFontStackHash = FontStackHasher()(baseFontStack); ft.formattedText = TaggedString(); for (std::size_t j = 0; j < formatted.sections.size(); j++) { @@ -122,8 +128,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, ft.formattedText->addSection(applyArabicShaping(util::convertUTF8ToUTF16(u8string)), section.fontScale ? *section.fontScale : 1.0, - section.fontStack ? FontStackHasher()(*section.fontStack) : baseFontStackHash); - + section.fontStack ? *section.fontStack : baseFontStack, + section.textColor); } @@ -273,6 +279,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, : layout.get(); const float textRepeatDistance = symbolSpacing / 2; + const auto evaluatedLayoutProperties = layout.evaluate(zoom, feature); IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { @@ -285,7 +292,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, // (1) render symbols that overlap into this tile // (2) approximate collision detection effects from neighboring symbols symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, - layout.evaluate(zoom, feature), layoutTextSize, + evaluatedLayoutProperties, layoutTextSize, textBoxScale, textPadding, textPlacement, textOffset, iconBoxScale, iconPadding, iconOffset, glyphPositions, indexedFeature, layoutFeatureIndex, feature.index, @@ -418,43 +425,14 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr sizeData = bucket->textSizeBinder->getVertexSizeData(feature); - bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, - symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor)); - symbolInstance.placedTextIndex = bucket->text.placedSymbols.size() - 1; - PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back(); - - bool firstHorizontal = true; - for (const auto& symbol : symbolInstance.horizontalGlyphQuads) { - size_t index = addSymbol( - bucket->text, sizeData, symbol, - symbolInstance.anchor, horizontalSymbol); - if (firstHorizontal) { - horizontalSymbol.vertexStartIndex = index; - firstHorizontal = false; - } - } + if (hasText && feature.formattedText) { + std::size_t index = addSymbolGlyphQuads(*bucket, symbolInstance, feature, symbolInstance.writingModes, symbolInstance.placedTextIndex, symbolInstance.horizontalGlyphQuads); if (symbolInstance.writingModes & WritingModeType::Vertical) { - bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, - symbolInstance.textOffset, WritingModeType::Vertical, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor)); - symbolInstance.placedVerticalTextIndex = bucket->text.placedSymbols.size() - 1; - - PlacedSymbol& verticalSymbol = bucket->text.placedSymbols.back(); - bool firstVertical = true; - - for (const auto& symbol : symbolInstance.verticalGlyphQuads) { - size_t index = addSymbol( - bucket->text, sizeData, symbol, - symbolInstance.anchor, verticalSymbol); - - if (firstVertical) { - verticalSymbol.vertexStartIndex = index; - firstVertical = false; - } - } + index = addSymbolGlyphQuads(*bucket, symbolInstance, feature, WritingModeType::Vertical, symbolInstance.placedVerticalTextIndex, symbolInstance.verticalGlyphQuads, index); } + + updatePaintPropertiesForSection(*bucket, feature, index); } if (hasIcon) { @@ -466,12 +444,11 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptricon.placedSymbols.back(); iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad, symbolInstance.anchor, iconSymbol); - } - } - for (auto& pair : bucket->paintProperties) { - pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {}); - pair.second.textBinders.populateVertexVectors(feature, bucket->text.vertices.elements(), {}, {}); + for (auto& pair : bucket->paintProperties) { + pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {}); + } + } } } @@ -489,12 +466,53 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr -size_t SymbolLayout::addSymbol(Buffer& buffer, - const Range sizeData, - const SymbolQuad& symbol, - const Anchor& labelAnchor, - PlacedSymbol& placedSymbol) { +void SymbolLayout::updatePaintPropertiesForSection(SymbolBucket& bucket, + const SymbolFeature& feature, + std::size_t sectionIndex) { + const auto& formattedSection = sectionOptionsToValue((*feature.formattedText).sectionAt(sectionIndex)); + for (auto& pair : bucket.paintProperties) { + pair.second.textBinders.populateVertexVectors(feature, bucket.text.vertices.elements(), {}, {}, formattedSection); + } +} + +std::size_t SymbolLayout::addSymbolGlyphQuads(SymbolBucket& bucket, + SymbolInstance& symbolInstance, + const SymbolFeature& feature, + WritingModeType writingMode, + optional& placedIndex, + const SymbolQuads& glyphQuads, + optional lastAddedSection) { + const Range sizeData = bucket.textSizeBinder->getVertexSizeData(feature); + const bool hasFormatSectionOverrides = bucket.hasFormatSectionOverrides(); + + 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)); + placedIndex = bucket.text.placedSymbols.size() - 1; + PlacedSymbol& placedSymbol = bucket.text.placedSymbols.back(); + + bool firstSymbol = true; + for (const auto& symbolQuad : glyphQuads) { + if (hasFormatSectionOverrides) { + if (lastAddedSection && *lastAddedSection != symbolQuad.sectionIndex) { + updatePaintPropertiesForSection(bucket, feature, *lastAddedSection); + } + lastAddedSection = symbolQuad.sectionIndex; + } + size_t index = addSymbol(bucket.text, sizeData, symbolQuad, symbolInstance.anchor, placedSymbol); + if (firstSymbol) { + placedSymbol.vertexStartIndex = index; + firstSymbol = false; + } + } + + return lastAddedSection ? *lastAddedSection : 0u; +} + +size_t SymbolLayout::addSymbol(SymbolBucket::Buffer& buffer, + const Range sizeData, + const SymbolQuad& symbol, + const Anchor& labelAnchor, + PlacedSymbol& placedSymbol) { constexpr const uint16_t vertexLength = 4; const auto &tl = symbol.tl; diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index ab6dc049a2..53c66d31fe 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -6,18 +6,15 @@ #include #include #include -#include -#include +#include #include #include -#include #include namespace mbgl { class BucketParameters; -class SymbolBucket; class Anchor; class RenderLayer; class PlacedSymbol; @@ -26,7 +23,7 @@ namespace style { class Filter; } // namespace style -class SymbolLayout : public Layout { +class SymbolLayout final : public Layout { public: SymbolLayout(const BucketParameters&, const std::vector&, @@ -62,12 +59,25 @@ private: void addToDebugBuffers(SymbolBucket&); // Adds placed items to the buffer. - template - size_t addSymbol(Buffer&, - const Range sizeData, - const SymbolQuad&, - const Anchor& labelAnchor, - PlacedSymbol& placedSymbol); + size_t addSymbol(SymbolBucket::Buffer&, + const Range sizeData, + const SymbolQuad&, + const Anchor& labelAnchor, + PlacedSymbol& placedSymbol); + + // Adds symbol quads to bucket and returns formatted section index of last + // added quad. + std::size_t addSymbolGlyphQuads(SymbolBucket&, + SymbolInstance&, + const SymbolFeature&, + WritingModeType, + optional& placedIndex, + const SymbolQuads&, + optional lastAddedSection = nullopt); + + void updatePaintPropertiesForSection(SymbolBucket&, + const SymbolFeature&, + std::size_t sectionIndex); // Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature, // which may reference data from this object. diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 3a3688a60b..9220235f1d 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -28,13 +28,17 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { for (const auto& pair : paintProperties_) { + auto layerPaintProperties = pair.second; + if (hasFormatSectionOverrides()) { + setPaintPropertyOverrides(layerPaintProperties); + } paintProperties.emplace( std::piecewise_construct, std::forward_as_tuple(pair.first), std::forward_as_tuple(PaintProperties { - pair.second, - { RenderSymbolLayer::iconPaintProperties(pair.second), zoom }, - { RenderSymbolLayer::textPaintProperties(pair.second), zoom } + layerPaintProperties, + { RenderSymbolLayer::iconPaintProperties(layerPaintProperties), zoom }, + { RenderSymbolLayer::textPaintProperties(layerPaintProperties), zoom } })); } } @@ -226,4 +230,23 @@ void SymbolBucket::sortFeatures(const float angle) { } } +void SymbolBucket::updatePaintProperties(const std::string& layerID, + style::SymbolPaintProperties::PossiblyEvaluated updated) { + if (hasFormatSectionOverrides()) { + SymbolLayerPaintPropertyOverrides::updateOverrides(paintProperties.at(layerID).evaluated, updated); + } + paintProperties.at(layerID).evaluated = updated; +} + +void SymbolBucket::setPaintPropertyOverrides(style::SymbolPaintProperties::PossiblyEvaluated& paint) { + SymbolLayerPaintPropertyOverrides::setOverrides(layout, paint); +} + +bool SymbolBucket::hasFormatSectionOverrides() { + if (!hasFormatSectionOverrides_) { + hasFormatSectionOverrides_= SymbolLayerPaintPropertyOverrides::hasOverrides(layout.get()); + } + return *hasFormatSectionOverrides_; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index 709e48dd2e..9764d870da 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -58,6 +58,10 @@ public: bool hasIconData() const; bool hasCollisionBoxData() const; bool hasCollisionCircleData() const; + bool hasFormatSectionOverrides(); + void updatePaintProperties(const std::string& layerID, + style::SymbolPaintProperties::PossiblyEvaluated); + void setPaintPropertyOverrides(style::SymbolPaintProperties::PossiblyEvaluated&); void updateOpacity(); void sortFeatures(const float angle); @@ -87,7 +91,7 @@ public: std::unique_ptr textSizeBinder; - struct TextBuffer { + struct Buffer { gfx::VertexVector vertices; gfx::VertexVector> dynamicVertices; gfx::VertexVector> opacityVertices; @@ -103,19 +107,8 @@ public: std::unique_ptr iconSizeBinder; - struct IconBuffer { - gfx::VertexVector vertices; - gfx::VertexVector> dynamicVertices; - gfx::VertexVector> opacityVertices; - gfx::IndexVector triangles; - SegmentVector segments; - std::vector placedSymbols; + struct IconBuffer : public Buffer { PremultipliedImage atlasImage; - - optional> vertexBuffer; - optional>> dynamicVertexBuffer; - optional>> opacityVertexBuffer; - optional indexBuffer; } icon; struct CollisionBuffer { @@ -139,6 +132,7 @@ public: uint32_t bucketInstanceId = 0; bool justReloaded = false; + optional hasFormatSectionOverrides_; std::shared_ptr> featureSortOrder; }; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index e523a869b2..43e3068ff0 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -411,7 +411,7 @@ void RenderSymbolLayer::sortRenderTiles(const TransformState& state) { void RenderSymbolLayer::updateBucketPaintProperties(Bucket* bucket) const { assert(bucket->supportsLayer(*baseImpl)); - static_cast(bucket)->paintProperties.at(getID()).evaluated = evaluated; + static_cast(bucket)->updatePaintProperties(getID(), evaluated); } } // namespace mbgl diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index 34600508af..dc5ab6ae22 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -95,7 +95,10 @@ public: virtual ~PaintPropertyBinder() = default; - virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) = 0; + virtual void populateVertexVector(const GeometryTileFeature& feature, + std::size_t length, const ImagePositions&, + const optional&, + const style::expression::Value&) = 0; virtual void upload(gfx::Context& context) = 0; virtual void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) = 0; virtual std::tuple>...> attributeBinding(const PossiblyEvaluatedType& currentValue) const = 0; @@ -114,7 +117,7 @@ public: : constant(std::move(constant_)) { } - void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&) override {} + void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&, const style::expression::Value&) override {} void upload(gfx::Context&) override {} void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; @@ -135,13 +138,13 @@ private: }; template -class ConstantCrossFadedPaintPropertyBinder : public PaintPropertyBinder,PossiblyEvaluatedPropertyValue>, As...> { +class ConstantCrossFadedPaintPropertyBinder final : public PaintPropertyBinder,PossiblyEvaluatedPropertyValue>, As...> { public: ConstantCrossFadedPaintPropertyBinder(Faded constant_) : constant(std::move(constant_)), constantPatternPositions({}) { } - void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&) override {} + void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&, const style::expression::Value&) override {} void upload(gfx::Context&) override {} void setPatternParameters(const optional& posA, const optional& posB, CrossfadeParameters&) override { @@ -171,7 +174,7 @@ private: }; template -class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder, A> { +class SourceFunctionPaintPropertyBinder final : public PaintPropertyBinder, A> { public: using BaseAttributeType = A; using BaseVertex = gfx::Vertex; @@ -183,8 +186,9 @@ public: defaultValue(std::move(defaultValue_)) { } void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; - void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) override { - auto evaluated = expression.evaluate(feature, defaultValue); + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&, const style::expression::Value& formattedSection) override { + using style::expression::EvaluationContext; + auto evaluated = expression.evaluate(EvaluationContext(&feature).withFormattedSection(&formattedSection), defaultValue); this->statistics.add(evaluated); auto value = attributeValue(evaluated); for (std::size_t i = vertexVector.elements(); i < length; ++i) { @@ -227,7 +231,7 @@ private: }; template -class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder, A> { +class CompositeFunctionPaintPropertyBinder final : public PaintPropertyBinder, A> { public: using AttributeType = ZoomInterpolatedAttributeType; @@ -240,8 +244,12 @@ public: zoomRange({zoom, zoom + 1}) { } void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; - void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) override { - Range range = expression.evaluate(zoomRange, feature, defaultValue); + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&, const style::expression::Value& formattedSection) override { + using style::expression::EvaluationContext; + Range range = { + expression.evaluate(EvaluationContext(zoomRange.min, &feature).withFormattedSection(&formattedSection), defaultValue), + expression.evaluate(EvaluationContext(zoomRange.max, &feature).withFormattedSection(&formattedSection), defaultValue), + }; this->statistics.add(range.min); this->statistics.add(range.max); AttributeValue value = zoomInterpolatedAttributeValue( @@ -292,7 +300,7 @@ private: }; template -class CompositeCrossFadedPaintPropertyBinder : public PaintPropertyBinder, PossiblyEvaluatedPropertyValue>, A1, A2> { +class CompositeCrossFadedPaintPropertyBinder final : public PaintPropertyBinder, PossiblyEvaluatedPropertyValue>, A1, A2> { public: using AttributeType = ZoomInterpolatedAttributeType; using AttributeType2 = ZoomInterpolatedAttributeType; @@ -313,7 +321,7 @@ public: crossfade = crossfade_; }; - void populateVertexVector(const GeometryTileFeature&, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies) override { + void populateVertexVector(const GeometryTileFeature&, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies, const style::expression::Value&) override { if (patternDependencies->mid.empty()) { // Unlike other propperties with expressions that evaluate to null, the default value for `*-pattern` properties is an empty @@ -474,9 +482,9 @@ public: PaintPropertyBinders(PaintPropertyBinders&&) = default; PaintPropertyBinders(const PaintPropertyBinders&) = delete; - void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies) { + void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies, const style::expression::Value& formattedSection = {}) { util::ignore({ - (binders.template get()->populateVertexVector(feature, length, patternPositions, patternDependencies), 0)... + (binders.template get()->populateVertexVector(feature, length, patternPositions, patternDependencies, formattedSection), 0)... }); } diff --git a/src/mbgl/style/conversion/function.cpp b/src/mbgl/style/conversion/function.cpp index 5877d0eb7c..79ad2fc7d8 100644 --- a/src/mbgl/style/conversion/function.cpp +++ b/src/mbgl/style/conversion/function.cpp @@ -41,7 +41,7 @@ bool hasTokens(const std::string& source) { std::unique_ptr convertTokenStringToFormatExpression(const std::string& source) { auto textExpression = convertTokenStringToExpression(source); std::vector sections; - sections.emplace_back(std::move(textExpression), nullopt, nullopt); + sections.emplace_back(std::move(textExpression), nullopt, nullopt, nullopt); return std::make_unique(sections); } diff --git a/src/mbgl/style/expression/dsl.cpp b/src/mbgl/style/expression/dsl.cpp index f5ff83a9e7..e7d90ba07b 100644 --- a/src/mbgl/style/expression/dsl.cpp +++ b/src/mbgl/style/expression/dsl.cpp @@ -189,13 +189,13 @@ std::unique_ptr concat(std::vector> inpu std::unique_ptr format(const char* value) { return std::make_unique(Formatted(value)); } - + std::unique_ptr format(std::unique_ptr input) { std::vector sections; - sections.emplace_back(std::move(input), nullopt, nullopt); + sections.emplace_back(std::move(input), nullopt, nullopt, nullopt); return std::make_unique(sections); } - + } // namespace dsl } // namespace expression } // namespace style diff --git a/src/mbgl/style/expression/format_expression.cpp b/src/mbgl/style/expression/format_expression.cpp index 144df4b160..b5e4ba62c4 100644 --- a/src/mbgl/style/expression/format_expression.cpp +++ b/src/mbgl/style/expression/format_expression.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include -#include +#include namespace mbgl { namespace style { @@ -10,15 +8,21 @@ namespace expression { FormatExpressionSection::FormatExpressionSection(std::unique_ptr text_, optional> fontScale_, - optional> textFont_) + optional> textFont_, + optional> textColor_) : text(std::move(text_)) { if (fontScale_) { fontScale = std::shared_ptr(std::move(*fontScale_)); } + if (textFont_) { textFont = std::shared_ptr(std::move(*textFont_)); } + + if (textColor_) { + textColor = std::shared_ptr(std::move(*textColor_)); + } } FormatExpression::FormatExpression(std::vector sections_) @@ -53,7 +57,7 @@ ParseResult FormatExpression::parse(const Convertible& value, ParsingContext& ct return ParseResult(); } - const optional fontScaleOption = objectMember(options, "font-scale"); + const optional fontScaleOption = objectMember(options, kFormattedSectionFontScale); ParseResult fontScale; if (fontScaleOption) { fontScale = ctx.parse(*fontScaleOption, 1, {type::Number}); @@ -62,7 +66,7 @@ ParseResult FormatExpression::parse(const Convertible& value, ParsingContext& ct } } - const optional textFontOption = objectMember(options, "text-font"); + const optional textFontOption = objectMember(options, kFormattedSectionTextFont); ParseResult textFont; if (textFontOption) { textFont = ctx.parse(*textFontOption, 1, {type::Array(type::String)}); @@ -70,7 +74,20 @@ ParseResult FormatExpression::parse(const Convertible& value, ParsingContext& ct return ParseResult(); } } - sections.emplace_back(std::move(*text), std::move(fontScale), std::move(textFont)); + + const optional textColorOption = objectMember(options, kFormattedSectionTextColor); + ParseResult textColor; + if (textColorOption) { + textColor = ctx.parse(*textColorOption, 1, {type::Color}); + if (!textColor) { + return ParseResult(); + } + } + + sections.emplace_back(std::move(*text), + std::move(fontScale), + std::move(textFont), + std::move(textColor)); } return ParseResult(std::make_unique(std::move(sections))); @@ -85,6 +102,9 @@ void FormatExpression::eachChild(const std::function& f if (section.textFont) { fn(**section.textFont); } + if (section.textColor) { + fn(**section.textColor); + } } } @@ -108,6 +128,10 @@ bool FormatExpression::operator==(const Expression& e) const { (!lhsSection.textFont && rhsSection.textFont)) { return false; } + if ((lhsSection.textColor && (!rhsSection.textColor || **lhsSection.textColor != **rhsSection.textColor)) || + (!lhsSection.textColor && rhsSection.textColor)) { + return false; + } } return true; } @@ -115,15 +139,18 @@ bool FormatExpression::operator==(const Expression& e) const { } mbgl::Value FormatExpression::serialize() const { - std::vector serialized{{ std::string("format") }}; + std::vector serialized{{ getOperator() }}; for (const auto& section : sections) { serialized.push_back(section.text->serialize()); std::unordered_map options; if (section.fontScale) { - options.emplace("font-scale", (*section.fontScale)->serialize()); + options.emplace(kFormattedSectionFontScale, (*section.fontScale)->serialize()); } if (section.textFont) { - options.emplace("text-font", (*section.textFont)->serialize()); + options.emplace(kFormattedSectionTextFont, (*section.textFont)->serialize()); + } + if (section.textColor) { + options.emplace(kFormattedSectionTextColor, (*section.textColor)->serialize()); } serialized.push_back(options); } @@ -164,7 +191,20 @@ EvaluationResult FormatExpression::evaluate(const EvaluationContext& params) con } evaluatedTextFont = *textFontValue; } - evaluatedSections.emplace_back(*evaluatedText, evaluatedFontScale, evaluatedTextFont); + + optional evaluatedTextColor; + if (section.textColor) { + auto textColorResult = (*section.textColor)->evaluate(params); + if (!textColorResult) { + return textColorResult.error(); + } + + evaluatedTextColor = fromExpressionValue(*textColorResult); + if (!evaluatedTextColor) { + return EvaluationError { "Format text-color option must evaluate to Color" }; + } + } + evaluatedSections.emplace_back(*evaluatedText, evaluatedFontScale, evaluatedTextFont, evaluatedTextColor); } return Formatted(evaluatedSections); } diff --git a/src/mbgl/style/expression/formatted.cpp b/src/mbgl/style/expression/formatted.cpp index 8232d0c698..3fa39b2cdc 100644 --- a/src/mbgl/style/expression/formatted.cpp +++ b/src/mbgl/style/expression/formatted.cpp @@ -1,18 +1,15 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include namespace mbgl { namespace style { - namespace expression { +const char* const kFormattedSectionFontScale = "font-scale"; +const char* const kFormattedSectionTextFont = "text-font"; +const char* const kFormattedSectionTextColor = "text-color"; + bool Formatted::operator==(const Formatted& other) const { if (other.sections.size() != sections.size()) { return false; @@ -22,14 +19,14 @@ bool Formatted::operator==(const Formatted& other) const { const auto& otherSection = other.sections.at(i); if (thisSection.text != otherSection.text || thisSection.fontScale != otherSection.fontScale || - thisSection.fontStack != otherSection.fontStack) { + thisSection.fontStack != otherSection.fontStack || + thisSection.textColor != otherSection.textColor) { return false; } } return true; } - - + std::string Formatted::toString() const { std::string result; for (const auto& section : sections) { @@ -37,7 +34,7 @@ std::string Formatted::toString() const { } return result; } - + } // namespace expression namespace conversion { @@ -65,6 +62,7 @@ optional Converter::operator()(const Convertible& value, E optional fontScale; optional textFont; + optional textColor; if (sectionLength > 1) { Convertible sectionParams = arrayMember(section, 1); if (!isObject(sectionParams)) { @@ -72,12 +70,12 @@ optional Converter::operator()(const Convertible& value, E return nullopt; } - optional fontScaleMember = objectMember(sectionParams, "font-scale"); + optional fontScaleMember = objectMember(sectionParams, kFormattedSectionFontScale); if (fontScaleMember) { fontScale = toDouble(*fontScaleMember); } - optional textFontMember = objectMember(sectionParams, "text-font"); + optional textFontMember = objectMember(sectionParams, kFormattedSectionTextFont); if (textFontMember) { if (isArray(*textFontMember)) { std::vector fontsVector; @@ -96,9 +94,17 @@ optional Converter::operator()(const Convertible& value, E return nullopt; } } + + optional textColorMember = objectMember(sectionParams, kFormattedSectionTextColor); + if (textColorMember) { + textColor = convert(*textColorMember, error); + if (!textColor) { + return nullopt; + } + } } - sections.push_back(FormattedSection(*sectionText, fontScale, textFont)); + sections.push_back(FormattedSection(*sectionText, fontScale, textFont, textColor)); } return Formatted(sections); } else if (optional result = toString(value)) { diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp index 3b20f49a86..9704168a41 100644 --- a/src/mbgl/style/expression/is_constant.cpp +++ b/src/mbgl/style/expression/is_constant.cpp @@ -17,18 +17,22 @@ bool isFeatureConstant(const Expression& expression) { return false; } else if (name == "has" && parameterCount && *parameterCount == 1) { return false; - } else if (0 == name.rfind(filter, 0)) { - // Legacy filters begin with "filter-" and are never constant. - return false; } else if ( name == "properties" || name == "geometry-type" || name == "id" ) { return false; + } else if (0u == name.rfind(filter, 0u)) { + // Legacy filters begin with "filter-" and are never constant. + return false; } } + if (expression.getKind() == Kind::FormatSectionOverride) { + return false; + } + if (expression.getKind() == Kind::CollatorExpression) { // Although the results of a Collator expression with fixed arguments // generally shouldn't change between executions, we can't serialize them diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index 792f858862..89dffdcd42 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -31,6 +31,11 @@ struct <%- camelize(property.name) %> : ColorRampProperty { <% } else { -%> struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> { static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } +<% if (isOverridable(property)) { -%> + static constexpr const char *name() { return "<%- property.name %>"; } + static constexpr auto expressionType() { return expression::type::<%- expressionType(property) %>{}; }; + template static bool hasOverride(const T& t) { return !!t.<%- camelizeWithLeadingLowercase(property.name) %>; }; +<% } -%> }; <% } -%> diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index 3dd1da1136..e35e7b0b9f 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -1,17 +1,24 @@ #include - #include namespace mbgl { namespace style { +bool SymbolLayer::Impl::hasFormatSectionOverrides() const { + if (!hasFormatSectionOverrides_) { + hasFormatSectionOverrides_ = SymbolLayerPaintPropertyOverrides::hasOverrides(layout.get()); + } + return *hasFormatSectionOverrides_; +} + bool SymbolLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { assert(other.getTypeInfo() == getTypeInfo()); const auto& impl = static_cast(other); return filter != impl.filter || visibility != impl.visibility || layout != impl.layout || - paint.hasDataDrivenPropertyDifference(impl.paint); + paint.hasDataDrivenPropertyDifference(impl.paint) || + (hasFormatSectionOverrides() && SymbolLayerPaintPropertyOverrides::hasPaintPropertyDifference(paint, impl.paint)); } void SymbolLayer::Impl::populateFontStack(std::set& fontStack) const { @@ -20,10 +27,10 @@ void SymbolLayer::Impl::populateFontStack(std::set& fontStack) const } layout.get().match( - [&] (Undefined) { + [&fontStack] (Undefined) { fontStack.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); }, - [&] (const FontStack& constant) { + [&fontStack] (const FontStack& constant) { fontStack.insert(constant); }, [&] (const auto& function) { diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index a5b0332f6c..f937fccaa8 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -3,10 +3,104 @@ #include #include #include +#include +#include +#include namespace mbgl { namespace style { +template +struct FormatSectionOverrides; + +template +struct FormatSectionOverrides> { + template + static void setOverride(const T& overrides, U& overridable) { + if (hasOverride(overrides.template get())) { + auto override = + std::make_unique>(Property::expressionType(), + std::move(overridable.template get()), + Property::name()); + PropertyExpression expr(std::move(override)); + overridable.template get() = PossiblyEvaluatedPropertyValue(std::move(expr)); + } + } + + template + static void setOverrides(const T& overrides, U& overridable) { + util::ignore({(setOverride(overrides, overridable), 0)...}); + } + + template + static void updateOverride(T& evaluated, U& updated) { + auto property = evaluated.template get(); + if (!property.isConstant()) { + const bool hasFormatSectionOverride = property.match( + [] (const style::PropertyExpression& e) { + return e.getExpression().getKind() == expression::Kind::FormatSectionOverride; + }, + [] (const auto&) { + return false; + }); + if (hasFormatSectionOverride) { + updated.template get() = std::move(property); + } + } + } + + template + static void updateOverrides(T& evaluated, U& updated) { + util::ignore({(updateOverride(evaluated, updated), 0)...}); + } + + template + static bool hasOverride(const FormattedProperty& formatted) { + return formatted.match( + [] (const TextField::Type& t) { + for (const auto& section : t.sections) { + if (Property::hasOverride(section)) { + return true; + } + } + return false; + }, + [] (const PropertyExpression& t) { + if (t.getExpression().getKind() == expression::Kind::FormatExpression) { + const auto* e = static_cast(&t.getExpression()); + for (const auto& section : e->getSections()) { + if (Property::hasOverride(section)) { + return true; + } + } + } + return false; + }, + [] (const auto&) { + return false; + } + ); + } + + template + static bool hasOverrides(const FormattedProperty& formatted) { + bool result = false; + util::ignore({ (result |= hasOverride(formatted))... }); + return result; + } + + template + static bool hasPaintPropertyDifference(const PaintProperties& lhs, const PaintProperties& rhs) { + bool result = false; + util::ignore({ (result |= lhs.template get().value.isConstant() && + rhs.template get().value.isConstant() && + (lhs.template get().value.asConstant() != rhs.template get().value.asConstant()))... }); + return result; + } +}; + +using SymbolLayerPaintPropertyOverrides = FormatSectionOverrides; + class SymbolLayer::Impl : public Layer::Impl { public: using Layer::Impl::Impl; @@ -19,6 +113,10 @@ public: SymbolPaintProperties::Transitionable paint; DECLARE_LAYER_TYPE_INFO; + +private: + bool hasFormatSectionOverrides() const; + mutable optional hasFormatSectionOverrides_; }; } // namespace style diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index d5bdce1f5d..c352ab8e77 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -229,8 +229,11 @@ struct TextOpacity : DataDrivenPaintProperty { +struct TextColor : DataDrivenPaintProperty { static Color defaultValue() { return Color::black(); } + static constexpr const char *name() { return "text-color"; } + static constexpr auto expressionType() { return expression::type::ColorType{}; }; + template static bool hasOverride(const T& t) { return !!t.textColor; }; }; struct TextHaloColor : DataDrivenPaintProperty { diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp index 0fcad30cc4..d98a1cbf31 100644 --- a/src/mbgl/style/layout_property.hpp +++ b/src/mbgl/style/layout_property.hpp @@ -16,6 +16,7 @@ public: using PossiblyEvaluatedType = T; using Type = T; static constexpr bool IsDataDriven = false; + static constexpr bool IsOverridable = false; }; template @@ -27,6 +28,7 @@ public: using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue; using Type = T; static constexpr bool IsDataDriven = true; + static constexpr bool IsOverridable = false; }; } // namespace style diff --git a/src/mbgl/style/light_impl.hpp b/src/mbgl/style/light_impl.hpp index f094c9d462..33db64ae3f 100644 --- a/src/mbgl/style/light_impl.hpp +++ b/src/mbgl/style/light_impl.hpp @@ -21,6 +21,7 @@ public: using PossiblyEvaluatedType = T; using Type = T; static constexpr bool IsDataDriven = false; + static constexpr bool IsOverridable = false; }; struct LightAnchor : LightProperty { diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index 343e689a32..7d398748f2 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -21,9 +21,10 @@ public: using PossiblyEvaluatedType = T; using Type = T; static constexpr bool IsDataDriven = false; + static constexpr bool IsOverridable = false; }; -template +template class DataDrivenPaintProperty { public: using TransitionableType = Transitionable>; @@ -32,6 +33,7 @@ public: using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue; using Type = T; static constexpr bool IsDataDriven = true; + static constexpr bool IsOverridable = isOverridable; using Attribute = A; using AttributeList = TypeList; @@ -48,6 +50,7 @@ public: using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue>; using Type = T; static constexpr bool IsDataDriven = true; + static constexpr bool IsOverridable = false; using Attribute = A1; using AttributeList = TypeList; @@ -64,6 +67,7 @@ public: using PossiblyEvaluatedType = Faded; using Type = T; static constexpr bool IsDataDriven = false; + static constexpr bool IsOverridable = false; }; /* @@ -84,6 +88,7 @@ public: using PossiblyEvaluatedType = Color; using Type = Color; static constexpr bool IsDataDriven = false; + static constexpr bool IsOverridable = false; static Color defaultValue() { return {}; } }; diff --git a/src/mbgl/style/properties.hpp b/src/mbgl/style/properties.hpp index d836735c65..7f58ff223d 100644 --- a/src/mbgl/style/properties.hpp +++ b/src/mbgl/style/properties.hpp @@ -99,6 +99,9 @@ public: template struct IsDataDriven : std::integral_constant {}; +template +struct IsOverridable : std::integral_constant {}; + template class Properties { public: @@ -122,6 +125,7 @@ public: using EvaluatedTypes = TypeList; using DataDrivenProperties = FilteredTypeList; + using OverridableProperties = FilteredTypeList; template using Tuple = IndexedTuple; diff --git a/src/mbgl/style/property_expression.cpp b/src/mbgl/style/property_expression.cpp new file mode 100644 index 0000000000..9ebecc4b40 --- /dev/null +++ b/src/mbgl/style/property_expression.cpp @@ -0,0 +1,68 @@ +#include + +namespace mbgl { +namespace style { + +PropertyExpressionBase::PropertyExpressionBase(std::unique_ptr expression_) + : expression(std::move(expression_)), + zoomCurve(expression::findZoomCurveChecked(expression.get())) { + isZoomConstant_ = expression::isZoomConstant(*expression); + isFeatureConstant_ = expression::isFeatureConstant(*expression); +} + +bool PropertyExpressionBase::isZoomConstant() const noexcept { + return isZoomConstant_; +} + +bool PropertyExpressionBase::isFeatureConstant() const noexcept { + return isFeatureConstant_; +} + +bool PropertyExpressionBase::canEvaluateWith(const expression::EvaluationContext& context) const noexcept { + if (context.zoom) { + if (context.feature != nullptr) { + return !isFeatureConstant(); + } + return !isZoomConstant() && isFeatureConstant(); + } + + if (context.feature != nullptr) { + return isZoomConstant() && !isFeatureConstant(); + } + + return true; +} + +float PropertyExpressionBase::interpolationFactor(const Range& inputLevels, const float inputValue) const noexcept { + return zoomCurve.match( + [](std::nullptr_t) { + assert(false); + return 0.0f; + }, + [&](const expression::Interpolate* z) { + return z->interpolationFactor(Range { inputLevels.min, inputLevels.max }, inputValue); + }, + [](const expression::Step*) { + return 0.0f; + } + ); +} + +Range PropertyExpressionBase::getCoveringStops(const float lower, const float upper) const noexcept { + return zoomCurve.match( + [](std::nullptr_t) { + assert(false); + return Range(0.0f, 0.0f); + }, + [&](auto z) { + return z->getCoveringStops(lower, upper); + } + ); +} + +const expression::Expression& PropertyExpressionBase::getExpression() const noexcept { + return *expression; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 034784dc24..c97b242c10 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -19,7 +19,7 @@ namespace mbgl { using GlyphID = char16_t; using GlyphIDs = std::set; - + // Note: this only works for the BMP GlyphRange getGlyphRange(GlyphID glyph); @@ -59,8 +59,8 @@ using GlyphMap = std::map; class PositionedGlyph { public: - explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_, FontStackHash font_, float scale_) - : glyph(glyph_), x(x_), y(y_), vertical(vertical_), font(font_), scale(scale_) + explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_, FontStackHash font_, float scale_, std::size_t sectionIndex_ = 0) + : glyph(glyph_), x(x_), y(y_), vertical(vertical_), font(font_), scale(scale_), sectionIndex(sectionIndex_) {} GlyphID glyph = 0; @@ -70,6 +70,8 @@ public: FontStackHash font = 0; float scale = 0.0; + // Maps positioned glyph to TaggedString section + std::size_t sectionIndex; }; enum class WritingModeType : uint8_t; diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 9d582f14d6..ec0045caad 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -172,7 +172,7 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText, br = util::matrixMultiply(matrix, br); } - quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset); + quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset, positionedGlyph.sectionIndex); } return quads; diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 44a35a5014..f41a4fec66 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -20,14 +20,16 @@ public: Point br_, Rect tex_, WritingModeType writingMode_, - Point glyphOffset_) + Point glyphOffset_, + size_t sectionIndex_ = 0) : tl(std::move(tl_)), tr(std::move(tr_)), bl(std::move(bl_)), br(std::move(br_)), tex(std::move(tex_)), writingMode(writingMode_), - glyphOffset(glyphOffset_) {} + glyphOffset(glyphOffset_), + sectionIndex(sectionIndex_){} Point tl; Point tr; @@ -36,6 +38,7 @@ public: Rect tex; WritingModeType writingMode; Point glyphOffset; + size_t sectionIndex; }; using SymbolQuads = std::vector; diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 3a6335955b..02dbf146e1 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -299,7 +299,8 @@ void shapeLines(Shaping& shaping, std::size_t lineStartIndex = shaping.positionedGlyphs.size(); for (std::size_t i = 0; i < line.length(); i++) { - const SectionOptions& section = line.getSection(i); + const std::size_t sectionIndex = line.getSectionIndex(i); + const SectionOptions& section = line.sectionAt(sectionIndex); char16_t codePoint = line.getCharCodeAt(i); auto glyphs = glyphMap.find(section.fontStackHash); if (glyphs == glyphMap.end()) { @@ -318,10 +319,10 @@ void shapeLines(Shaping& shaping, const Glyph& glyph = **it->second; if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(codePoint)) { - shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale); + shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale, sectionIndex); x += glyph.metrics.advance * section.scale + spacing; } else { - shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale); + shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale, sectionIndex); x += verticalHeight * section.scale + spacing; } } diff --git a/src/mbgl/text/tagged_string.cpp b/src/mbgl/text/tagged_string.cpp index 851e011c4f..8c4e3b02e8 100644 --- a/src/mbgl/text/tagged_string.cpp +++ b/src/mbgl/text/tagged_string.cpp @@ -1,11 +1,12 @@ #include +#include #include namespace mbgl { -void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStackHash fontStack) { +void TaggedString::addSection(const std::u16string& sectionText, double scale, FontStack fontStack, optional textColor) { styledText.first += sectionText; - sections.emplace_back(scale, fontStack); + sections.emplace_back(scale, fontStack, std::move(textColor)); styledText.second.resize(styledText.first.size(), sections.size() - 1); } @@ -26,7 +27,7 @@ void TaggedString::trim() { double TaggedString::getMaxScale() const { double maxScale = 0.0; for (std::size_t i = 0; i < styledText.first.length(); i++) { - maxScale = std::max(maxScale, getSection(i).scale); + maxScale = util::max(maxScale, getSection(i).scale); } return maxScale; } diff --git a/src/mbgl/text/tagged_string.hpp b/src/mbgl/text/tagged_string.hpp index 476c2225f0..2607e10889 100644 --- a/src/mbgl/text/tagged_string.hpp +++ b/src/mbgl/text/tagged_string.hpp @@ -1,17 +1,23 @@ #pragma once -#include #include +#include +#include namespace mbgl { struct SectionOptions { - SectionOptions(double scale_, FontStackHash fontStackHash_) - : scale(scale_), fontStackHash(fontStackHash_) + SectionOptions(double scale_, FontStack fontStack_, optional textColor_ = nullopt) + : scale(scale_), + fontStackHash(FontStackHasher()(fontStack_)), + fontStack(std::move(fontStack_)), + textColor(std::move(textColor_)) {} double scale; FontStackHash fontStackHash; + FontStack fontStack; + optional textColor; }; /** @@ -71,7 +77,11 @@ struct TaggedString { return styledText; } - void addSection(const std::u16string& text, double scale, FontStackHash fontStack); + void addSection(const std::u16string& text, + double scale, + FontStack fontStack, + optional textColor_ = nullopt); + const SectionOptions& sectionAt(std::size_t index) const { return sections.at(index); } @@ -88,7 +98,7 @@ struct TaggedString { void trim(); void verticalizePunctuation(); - + private: StyledText styledText; std::vector sections; -- cgit v1.2.1