From f901e776b3e63aaaa6bc0cc4476624bf84127fe6 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Tue, 28 Feb 2017 19:54:24 -0800 Subject: [core] Implement data-driven styling for {text,icon}-{color,opacity,halo-color,halo-blur,halo-width} (#7939) * Add symbol dds attributes and adapt style code generation * Update to mapbox-gl-js/master * Refactor SymbolFeature as a subclass of GeometryTileFeature Prepares for enabling DDS on symbol paint properties by allowing the SymbolFeatures, which we keep around after constructing SymbolLayout, to be used in evaluating data-driven paint properties later in the layout process. * Draft approach for splitting icon/text paint properties The `Program` types are set up to bind GL attributes to each of the data-driven paint properties specified in the `PaintProperties` type provided. Since `SymbolPaintProperties` specifies both `Text*` and `Icon*` properties, the symbolIcon, symbolIconSDF, and symbolGlyph programs each attempt to bind roughly double the number of attributes that they actually need. This change addresses this by: - Adding the more specific `IconPaintProperties` and `TextPaintProperties` types, which are subsets of the full `SymbolPaintProperties`. - The symbol layer continues to use its `SymbolPaintProperties paint` member to track layer property state, but it provides helpers that construct objects of each the specific `{Icon,Text}PaintProperties::Evaluated` type, for use by the painter. - The three symbol programs instantiate `Program<>` using the appropriate `{Icon,Text}PaintProperties` type. * check in generated style code * Populate paint buffers for symbol DDS properties * Address first round of review comments * Refactor VectorTile{Layer,Feature} to explicitly share data * Update submodule --- src/mbgl/layout/symbol_feature.hpp | 17 ++- src/mbgl/layout/symbol_instance.cpp | 5 +- src/mbgl/layout/symbol_instance.hpp | 3 +- src/mbgl/layout/symbol_layout.cpp | 51 +++++---- src/mbgl/layout/symbol_layout.hpp | 7 +- src/mbgl/programs/attributes.hpp | 46 ++++++++ src/mbgl/programs/programs.hpp | 4 +- src/mbgl/programs/symbol_program.cpp | 88 ++++----------- src/mbgl/programs/symbol_program.hpp | 77 +++++++++---- src/mbgl/renderer/painter_symbol.cpp | 46 +++++--- src/mbgl/renderer/symbol_bucket.cpp | 12 +- src/mbgl/renderer/symbol_bucket.hpp | 10 +- src/mbgl/shaders/symbol_icon.cpp | 13 ++- src/mbgl/shaders/symbol_sdf.cpp | 63 +++++++++-- src/mbgl/style/layers/symbol_layer.cpp | 132 ++++++++++++++-------- src/mbgl/style/layers/symbol_layer_impl.cpp | 56 ++++++--- src/mbgl/style/layers/symbol_layer_impl.hpp | 44 +++++--- src/mbgl/style/layers/symbol_layer_properties.hpp | 20 ++-- src/mbgl/text/shaping.cpp | 9 +- src/mbgl/text/shaping.hpp | 3 +- src/mbgl/tile/vector_tile.cpp | 70 +++++++----- 21 files changed, 503 insertions(+), 273 deletions(-) (limited to 'src') diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index e55995f952..f4dc1680bc 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -8,14 +8,23 @@ namespace mbgl { -class SymbolFeature { +class SymbolFeature : public GeometryTileFeature { public: - FeatureType type; + SymbolFeature(std::unique_ptr feature_) : + feature(std::move(feature_)), + geometry(feature->getGeometries()) // we need a mutable copy of the geometry for mergeLines() + {} + + FeatureType getType() const override { return feature->getType(); } + optional getValue(const std::string& key) const override { return feature->getValue(key); }; + std::unordered_map getProperties() const override { return feature->getProperties(); }; + optional getID() const override { return feature->getID(); }; + GeometryCollection getGeometries() const override { return geometry; }; + + std::unique_ptr feature; GeometryCollection geometry; optional text; optional icon; - std::array iconOffset; - float iconRotation; std::size_t index; }; diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 4f425641e7..d81783b2f6 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -10,7 +10,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const bool addToBuffers, const uint32_t index_, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedFeature) : + const GlyphPositions& face, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : point(anchor.point), index(index_), hasText(shapedTextOrientations.first || shapedTextOrientations.second), @@ -18,7 +18,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, // Create the collision features that will be used to check whether this symbol instance can be placed textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature) { + iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_) { // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 2dbb3bac23..532a4d30d8 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -17,7 +17,7 @@ public: const style::SymbolLayoutProperties::Evaluated&, const bool inside, const uint32_t index, const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedfeature); + const GlyphPositions& face, const IndexedSubfeature& indexedfeature, const std::size_t featureIndex); Point point; uint32_t index; @@ -28,6 +28,7 @@ public: CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; WritingModeType writingModes; + std::size_t featureIndex; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index cf9e784c26..3a2c082ad8 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -87,7 +87,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } for (const auto& layer : layers) { - layerPaintProperties.emplace(layer->getID(), layer->as()->impl->paint.evaluated); + layerPaintProperties.emplace(layer->getID(), std::make_pair( + layer->as()->impl->iconPaintProperties(), + layer->as()->impl->textPaintProperties() + )); } // Determine and load glyph ranges @@ -96,12 +99,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, auto feature = sourceLayer.getFeature(i); if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) continue; + + SymbolFeature ft(std::move(feature)); - SymbolFeature ft; ft.index = i; - auto getValue = [&feature](const std::string& key) -> std::string { - auto value = feature->getValue(key); + auto getValue = [&ft](const std::string& key) -> std::string { + auto value = ft.getValue(key); if (!value) return std::string(); if (value->is()) @@ -118,12 +122,12 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, }; if (hasText) { - std::string u8string = layout.evaluate(zoom, *feature); + std::string u8string = layout.evaluate(zoom, ft); if (layout.get().isConstant()) { u8string = util::replaceTokens(u8string, getValue); } - auto textTransform = layout.evaluate(zoom, *feature); + auto textTransform = layout.evaluate(zoom, ft); if (textTransform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); @@ -144,13 +148,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { ft.icon = util::replaceTokens(layout.get(), getValue); - ft.iconOffset = layout.evaluate(zoom, *feature); - ft.iconRotation = layout.evaluate(zoom, *feature) * util::DEG2RAD; } if (ft.text || ft.icon) { - ft.type = feature->getType(); - ft.geometry = feature->getGeometries(); features.push_back(std::move(ft)); } } @@ -229,7 +229,8 @@ void SymbolLayout::prepare(uintptr_t tileUID, const bool textAlongLine = layout.get() == AlignmentType::Map && layout.get() == SymbolPlacementType::Line; - for (const auto& feature : features) { + for (auto it = features.begin(); it != features.end(); ++it) { + auto& feature = *it; if (feature.geometry.empty()) continue; std::pair shapedTextOrientations; @@ -273,7 +274,9 @@ void SymbolLayout::prepare(uintptr_t tileUID, if (feature.icon) { auto image = spriteAtlas.getIcon(*feature.icon); if (image) { - shapedIcon = shapeIcon(*image, feature); + shapedIcon = shapeIcon(*image, + layout.evaluate(zoom, feature), + layout.evaluate(zoom, feature) * util::DEG2RAD); assert((*image).spriteImage); if ((*image).spriteImage->sdf) { sdfIcons = true; @@ -288,15 +291,17 @@ void SymbolLayout::prepare(uintptr_t tileUID, // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(feature, shapedTextOrientations, shapedIcon, face); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, face); } + + feature.geometry.clear(); } - features.clear(); compareText.clear(); } -void SymbolLayout::addFeature(const SymbolFeature& feature, +void SymbolLayout::addFeature(const std::size_t index, + const SymbolFeature& feature, const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face) { @@ -345,8 +350,10 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, - face, indexedFeature); + face, indexedFeature, index); }; + + const auto& type = feature.getType(); if (layout.get() == SymbolPlacementType::Line) { auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT); @@ -368,7 +375,7 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, } } } - } else if (feature.type == FeatureType::Polygon) { + } else if (type == FeatureType::Polygon) { for (const auto& polygon : classifyRings(feature.geometry)) { Polygon poly; for (const auto& ring : polygon) { @@ -384,12 +391,12 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, Anchor anchor(poi.x, poi.y, 0, minScale); addSymbolInstance(polygon[0], anchor); } - } else if (feature.type == FeatureType::LineString) { + } else if (type == FeatureType::LineString) { for (const auto& line : feature.geometry) { Anchor anchor(line[0].x, line[0].y, 0, minScale); addSymbolInstance(line, anchor); } - } else if (feature.type == FeatureType::Point) { + } else if (type == FeatureType::Point) { for (const auto& points : feature.geometry) { for (const auto& point : points) { Anchor anchor(point.x, point.y, 0, minScale); @@ -503,6 +510,12 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } + + const auto& feature = features.at(symbolInstance.featureIndex); + for (auto& pair : bucket->paintPropertyBinders) { + pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); + pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); + } } if (collisionTile.config.debug) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index dbfdad22d9..491d0078da 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -51,10 +52,12 @@ public: State state = Pending; - std::unordered_map layerPaintProperties; + std::unordered_map> layerPaintProperties; private: - void addFeature(const SymbolFeature&, + void addFeature(const size_t, + const SymbolFeature&, const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face); diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index c4cc5dea8b..bb90f2c13c 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -64,6 +64,34 @@ struct a_color : gl::Attribute, 4> { } }; +// used in the symbol sdf shader +struct a_fill_color : gl::Attribute, 4> { + static auto name() { return "a_fill_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized(color.r), + gl::Normalized(color.g), + gl::Normalized(color.b), + gl::Normalized(color.a) + }}; + } +}; + +// used in the symbol sdf shader +struct a_halo_color : gl::Attribute, 4> { + static auto name() { return "a_halo_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized(color.r), + gl::Normalized(color.g), + gl::Normalized(color.b), + gl::Normalized(color.a) + }}; + } +}; + struct a_stroke_color : gl::Attribute, 4> { static auto name() { return "a_stroke_color"; } @@ -171,5 +199,23 @@ struct a_offset<1> : gl::Attribute { } }; +struct a_halo_width : gl::Attribute { + static auto name() { return "a_halo_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_halo_blur : gl::Attribute { + static auto name() { return "a_halo_blur"; } + + static Value value(float blur) { + return {{ blur }}; + } +}; + + + } // namespace attributes } // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index dd71c2ce97..742c5a221b 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -40,8 +40,8 @@ public: LinePatternProgram linePattern; RasterProgram raster; SymbolIconProgram symbolIcon; - SymbolSDFProgram symbolIconSDF; - SymbolSDFProgram symbolGlyph; + SymbolSDFIconProgram symbolIconSDF; + SymbolSDFTextProgram symbolGlyph; DebugProgram debug; CollisionBoxProgram collisionBox; diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 3f59000b94..19fe2bc2f6 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace mbgl { @@ -19,6 +20,7 @@ Values makeValues(const style::SymbolPropertyValues& values, std::array extrudeScale; const float scale = values.paintSize / values.sdfScale; + if (values.pitchAlignment == AlignmentType::Map) { extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale); } else { @@ -27,7 +29,7 @@ Values makeValues(const style::SymbolPropertyValues& values, pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance() }}; } - + // adjust min/max zooms for variable font sies float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); @@ -35,7 +37,6 @@ Values makeValues(const style::SymbolPropertyValues& values, uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, - uniforms::u_opacity::Value{ values.opacity }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, @@ -62,84 +63,37 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, ); } -static SymbolSDFProgram::UniformValues makeSDFValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio, - Color color, - float buffer, - float gammaAdjust) -{ - // The default gamma value has to be adjust for the current pixelratio so that we're not - // drawing blurry font on retina screens. - const float gammaBase = 0.105 * values.sdfScale / values.paintSize / pixelRatio; - const float gammaScale = (values.pitchAlignment == AlignmentType::Map - ? 1.0 / std::cos(state.getPitch()) - : 1.0) / state.getCameraToCenterDistance(); - - return makeValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - uniforms::u_color::Value{ color }, - uniforms::u_buffer::Value{ buffer }, - uniforms::u_gamma::Value{ (gammaBase + gammaAdjust) * gammaScale }, - uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map } - ); -} - -SymbolSDFProgram::UniformValues -SymbolSDFProgram::haloUniformValues(const style::SymbolPropertyValues& values, +template +typename SymbolSDFProgram::UniformValues SymbolSDFProgram::uniformValues(const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, - float pixelRatio) + const SymbolSDFPart part) { const float scale = values.paintSize / values.sdfScale; - const float sdfPx = 8.0f; - const float blurOffset = 1.19f; - const float haloOffset = 6.0f; - - return makeSDFValues( + + const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map + ? std::cos(state.getPitch()) + : 1.0) * state.getCameraToCenterDistance(); + + return makeValues::UniformValues>( values, texsize, pixelsToGLUnits, tile, state, - pixelRatio, - values.haloColor, - (haloOffset - values.haloWidth / scale) / sdfPx, - values.haloBlur * blurOffset / scale / sdfPx + uniforms::u_font_scale::Value{ scale }, + uniforms::u_gamma_scale::Value{ gammaScale }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, + uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, + uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, + uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } -SymbolSDFProgram::UniformValues -SymbolSDFProgram::foregroundUniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio) -{ - return makeSDFValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - pixelRatio, - values.color, - (256.0f - 64.0f) / 256.0f, - 0 - ); -} +template class SymbolSDFProgram; +template class SymbolSDFProgram; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index e0e90f0fa4..0537c25a2c 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -27,9 +28,10 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_buffer); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma); MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< @@ -75,14 +77,13 @@ class SymbolIconProgram : public Program< SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture>, - style::SymbolPaintProperties> + style::IconPaintProperties> { public: using Program::Program; @@ -94,47 +95,73 @@ public: const TransformState&); }; +enum class SymbolSDFPart { + Fill = 1, + Halo = 0 +}; + +template class SymbolSDFProgram : public Program< shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_color, - uniforms::u_buffer, - uniforms::u_gamma, + uniforms::u_font_scale, + uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map>, - style::SymbolPaintProperties> + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties> { public: - using Program::Program; + using BaseProgram = Program, + PaintProperties>; + + using UniformValues = typename BaseProgram::UniformValues; + - static UniformValues haloUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); - - static UniformValues foregroundUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); + + using BaseProgram::BaseProgram; + + static UniformValues uniformValues(const style::SymbolPropertyValues&, + const Size& texsize, + const std::array& pixelsToGLUnits, + const RenderTile&, + const TransformState&, + const SymbolSDFPart); }; +using SymbolSDFIconProgram = SymbolSDFProgram; +using SymbolSDFTextProgram = SymbolSDFProgram; + using SymbolLayoutVertex = SymbolLayoutAttributes::Vertex; -using SymbolAttributes = SymbolIconProgram::Attributes; +using SymbolIconAttributes = SymbolIconProgram::Attributes; +using SymbolTextAttributes = SymbolSDFTextProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 0113c15a08..48c2e7ff66 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -33,7 +33,9 @@ void Painter::renderSymbol(PaintParameters& parameters, auto draw = [&] (auto& program, auto&& uniformValues, const auto& buffers, - const SymbolPropertyValues& values_) + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) { // We clip symbols to their tile extent in still mode. const bool needsClipping = frame.mapMode == MapMode::Still; @@ -52,14 +54,15 @@ void Painter::renderSymbol(PaintParameters& parameters, *buffers.vertexBuffer, *buffers.indexBuffer, buffers.segments, - bucket.paintPropertyBinders.at(layer.getID()), - layer.impl->paint.evaluated, + binders, + paintProperties, state.getZoom() ); }; if (bucket.hasIconData()) { auto values = layer.impl->iconPropertyValues(layout); + auto paintPropertyValues = layer.impl->iconPaintProperties(); SpriteAtlas& atlas = *layer.impl->spriteAtlas; const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear; @@ -69,24 +72,30 @@ void Painter::renderSymbol(PaintParameters& parameters, const Size texsize = atlas.getSize(); if (bucket.sdfIcons) { - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } @@ -94,21 +103,26 @@ void Painter::renderSymbol(PaintParameters& parameters, glyphAtlas->bind(context, 0); auto values = layer.impl->textPropertyValues(layout); + auto paintPropertyValues = layer.impl->textPaintProperties(); const Size texsize = glyphAtlas->getSize(); - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } } diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 9d4bde9d07..fa4178dda1 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -9,7 +9,8 @@ namespace mbgl { using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, - const std::unordered_map& layerPaintProperties, + const std::unordered_map>& layerPaintProperties, float zoom, bool sdfIcons_, bool iconsNeedLinear_) @@ -17,8 +18,10 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_) { for (const auto& pair : layerPaintProperties) { - paintPropertyBinders.emplace(pair.first, - SymbolIconProgram::PaintPropertyBinders(pair.second, zoom)); + paintPropertyBinders.emplace(pair.first, std::make_pair( + SymbolIconProgram::PaintPropertyBinders(pair.second.first, zoom), + SymbolSDFTextProgram::PaintPropertyBinders(pair.second.second, zoom) + )); } } @@ -39,7 +42,8 @@ void SymbolBucket::upload(gl::Context& context) { } for (auto& pair : paintPropertyBinders) { - pair.second.upload(context); + pair.second.first.upload(context); + pair.second.second.upload(context); } uploaded = true; diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 0b40bb34ae..dcf3f5f495 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -17,7 +17,7 @@ namespace mbgl { class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::Evaluated, - const std::unordered_map&, + const std::unordered_map>&, float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -33,12 +33,14 @@ public: const bool sdfIcons; const bool iconsNeedLinear; - std::unordered_map paintPropertyBinders; + std::unordered_map> paintPropertyBinders; struct TextBuffer { gl::VertexVector vertices; gl::IndexVector triangles; - gl::SegmentVector segments; + gl::SegmentVector segments; optional> vertexBuffer; optional> indexBuffer; @@ -47,7 +49,7 @@ public: struct IconBuffer { gl::VertexVector vertices; gl::IndexVector triangles; - gl::SegmentVector segments; + gl::SegmentVector segments; optional> vertexBuffer; optional> indexBuffer; diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index eca9342d54..e6728e15de 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -66,6 +66,10 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -80,6 +84,8 @@ varying vec2 v_tex; varying vec2 v_fade_tex; void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -122,13 +128,16 @@ precision mediump float; #endif uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp float u_opacity; + +varying lowp float opacity; varying vec2 v_tex; varying vec2 v_fade_tex; void main() { - lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * u_opacity; + + + lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity; gl_FragColor = texture2D(u_texture, v_tex) * alpha; #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 7554597893..e087242bf8 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -68,6 +68,26 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_fill_color_t; +attribute lowp vec4 a_fill_color_min; +attribute lowp vec4 a_fill_color_max; +varying lowp vec4 fill_color; +uniform lowp float a_halo_color_t; +attribute lowp vec4 a_halo_color_min; +attribute lowp vec4 a_halo_color_max; +varying lowp vec4 halo_color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_halo_width_t; +attribute lowp float a_halo_width_min; +attribute lowp float a_halo_width_max; +varying lowp float halo_width; +uniform lowp float a_halo_blur_t; +attribute lowp float a_halo_blur_min; +attribute lowp float a_halo_blur_max; +varying lowp float halo_blur; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -87,6 +107,12 @@ varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + fill_color = mix(a_fill_color_min, a_fill_color_max, a_fill_color_t); + halo_color = mix(a_halo_color_min, a_halo_color_max, a_halo_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + halo_width = mix(a_halo_width_min, a_halo_width_max, a_halo_width_t); + halo_blur = mix(a_halo_blur_min, a_halo_blur_max, a_halo_blur_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -163,24 +189,47 @@ precision mediump float; #endif #endif +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO + +uniform bool u_is_halo; +varying lowp vec4 fill_color; +varying lowp vec4 halo_color; +varying lowp float opacity; +varying lowp float halo_width; +varying lowp float halo_blur; + uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp vec4 u_color; -uniform lowp float u_opacity; -uniform lowp float u_buffer; -uniform highp float u_gamma; +uniform lowp float u_font_scale; +uniform highp float u_gamma_scale; varying vec2 v_tex; varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + + + + + + + lowp vec4 color = fill_color; + lowp float gamma = EDGE_GAMMA / u_gamma_scale; + lowp float buff = (256.0 - 64.0) / 256.0; + if (u_is_halo) { + color = halo_color; + gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale; + buff = (6.0 - halo_width / u_font_scale) / SDF_PX; + } + lowp float dist = texture2D(u_texture, v_tex).a; lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; - highp float gamma = u_gamma * v_gamma_scale; - highp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha; + highp float gamma_scaled = gamma * v_gamma_scale; + highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha; - gl_FragColor = u_color * (alpha * u_opacity); + gl_FragColor = color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR gl_FragColor = vec4(1.0); diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 6364091207..d85b8c00e6 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -542,95 +542,115 @@ void SymbolLayer::setTextOptional(PropertyValue value) { // Paint properties -PropertyValue SymbolLayer::getDefaultIconOpacity() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconOpacity() { return { 1 }; } -PropertyValue SymbolLayer::getIconOpacity(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconOpacity(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconOpacity(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconOpacity(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconOpacity(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconColor() { return { Color::black() }; } -PropertyValue SymbolLayer::getIconColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloColor() { return { {} }; } -PropertyValue SymbolLayer::getIconHaloColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloWidth() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloWidth() { return { 0 }; } -PropertyValue SymbolLayer::getIconHaloWidth(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloWidth(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloWidth(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloWidth(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloBlur() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloBlur() { return { 0 }; } -PropertyValue SymbolLayer::getIconHaloBlur(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloBlur(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloBlur(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloBlur(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional& klass) { @@ -671,99 +691,119 @@ void SymbolLayer::setIconTranslateAnchor(PropertyValue valu impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue SymbolLayer::getDefaultTextOpacity() { - return { 1 }; -} - void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getTextOpacity(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getDefaultTextOpacity() { + return { 1 }; +} + +DataDrivenPropertyValue SymbolLayer::getTextOpacity(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextOpacity(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextOpacity(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextOpacity(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); -} - -PropertyValue SymbolLayer::getDefaultTextColor() { - return { Color::black() }; + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getTextColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getDefaultTextColor() { + return { Color::black() }; +} + +DataDrivenPropertyValue SymbolLayer::getTextColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloColor() { return { {} }; } -PropertyValue SymbolLayer::getTextHaloColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloWidth() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloWidth() { return { 0 }; } -PropertyValue SymbolLayer::getTextHaloWidth(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloWidth(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloWidth(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloWidth(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloBlur() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloBlur() { return { 0 }; } -PropertyValue SymbolLayer::getTextHaloBlur(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloBlur(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloBlur(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloBlur(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional& klass) { diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index 32547e465a..ff59b14d65 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -16,9 +16,14 @@ bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) // text-size and icon-size are layout properties but they also need to be evaluated as paint properties: iconSize = layout.evaluate(parameters); textSize = layout.evaluate(parameters); - - passes = ((paint.evaluated.get() > 0 && (paint.evaluated.get().a > 0 || paint.evaluated.get().a > 0) && iconSize > 0) - || (paint.evaluated.get() > 0 && (paint.evaluated.get().a > 0 || paint.evaluated.get().a > 0) && textSize > 0)) + + auto hasIconOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || + paint.evaluated.get().constantOr(Color::black()).a > 0; + auto hasTextOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || + paint.evaluated.get().constantOr(Color::black()).a > 0; + + passes = ((paint.evaluated.get().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (paint.evaluated.get().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); @@ -38,20 +43,43 @@ std::unique_ptr SymbolLayer::Impl::createLayout(const BucketParame *spriteAtlas); } -SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { - return SymbolPropertyValues { - layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get(), - layout_.get(), +IconPaintProperties::Evaluated SymbolLayer::Impl::iconPaintProperties() const { + return IconPaintProperties::Evaluated { paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), + paint.evaluated.get() + }; +} + +TextPaintProperties::Evaluated SymbolLayer::Impl::textPaintProperties() const { + return TextPaintProperties::Evaluated { + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get() + }; +} + + +SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { + return SymbolPropertyValues { + layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment + layout_.get(), + layout_.get(), + paint.evaluated.get(), paint.evaluated.get(), iconSize, - 1.0f + 1.0f, + paint.evaluated.get().constantOr(Color::black()).a > 0 && + paint.evaluated.get().constantOr(1), + paint.evaluated.get().constantOr(Color::black()).a > 0 }; } @@ -60,15 +88,13 @@ SymbolPropertyValues SymbolLayer::Impl::textPropertyValues(const SymbolLayoutPro layout_.get(), layout_.get(), layout_.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), textSize, - 24.0f + 24.0f, + paint.evaluated.get().constantOr(Color::black()).a > 0 && + paint.evaluated.get().constantOr(1), + paint.evaluated.get().constantOr(Color::black()).a > 0 }; } diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index c00c2b0bba..1e9f05e4c7 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -10,6 +11,30 @@ class SpriteAtlas; class SymbolLayout; namespace style { + + +// {icon,text}-specific paint-property packs for use in the symbol Programs. +// Since each program deals either with icons or text, using a smaller property set +// lets us avoid unnecessarily binding attributes for properties the program wouldn't use. +class IconPaintProperties : public PaintProperties< + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor +> {}; + +class TextPaintProperties : public PaintProperties< + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor +> {}; // Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties // for genericity over icons vs. text. @@ -21,24 +46,14 @@ public: float layoutSize; // Paint - float opacity; - Color color; - Color haloColor; - float haloWidth; - float haloBlur; std::array translate; TranslateAnchorType translateAnchor; float paintSize; float sdfScale; // Constant (1.0 or 24.0) - - bool hasHalo() const { - return haloColor.a > 0.0f && haloWidth > 0.0f; - } - - bool hasForeground() const { - return color.a > 0.0f; - } + + bool hasHalo; + bool hasFill; }; class SymbolLayer::Impl : public Layer::Impl { @@ -54,6 +69,9 @@ public: std::unique_ptr createLayout(const BucketParameters&, const std::vector&, const GeometryTileLayer&) const; + IconPaintProperties::Evaluated iconPaintProperties() const; + TextPaintProperties::Evaluated textPaintProperties() const; + SymbolPropertyValues iconPropertyValues(const SymbolLayoutProperties::Evaluated&) const; SymbolPropertyValues textPropertyValues(const SymbolLayoutProperties::Evaluated&) const; diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index 3bdae377ea..f2b7bfa00f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -180,23 +180,23 @@ struct TextOptional : LayoutProperty { static bool defaultValue() { return false; } }; -struct IconOpacity : PaintProperty { +struct IconOpacity : DataDrivenPaintProperty { static float defaultValue() { return 1; } }; -struct IconColor : PaintProperty { +struct IconColor : DataDrivenPaintProperty { static Color defaultValue() { return Color::black(); } }; -struct IconHaloColor : PaintProperty { +struct IconHaloColor : DataDrivenPaintProperty { static Color defaultValue() { return {}; } }; -struct IconHaloWidth : PaintProperty { +struct IconHaloWidth : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; -struct IconHaloBlur : PaintProperty { +struct IconHaloBlur : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; @@ -208,23 +208,23 @@ struct IconTranslateAnchor : PaintProperty { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -struct TextOpacity : PaintProperty { +struct TextOpacity : DataDrivenPaintProperty { static float defaultValue() { return 1; } }; -struct TextColor : PaintProperty { +struct TextColor : DataDrivenPaintProperty { static Color defaultValue() { return Color::black(); } }; -struct TextHaloColor : PaintProperty { +struct TextHaloColor : DataDrivenPaintProperty { static Color defaultValue() { return {}; } }; -struct TextHaloWidth : PaintProperty { +struct TextHaloWidth : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; -struct TextHaloBlur : PaintProperty { +struct TextHaloBlur : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index b43ba0220c..e68566d419 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -3,16 +3,15 @@ namespace mbgl { -PositionedIcon shapeIcon(const SpriteAtlasElement& image, - const SymbolFeature& feature) { - float dx = feature.iconOffset[0]; - float dy = feature.iconOffset[1]; +PositionedIcon shapeIcon(const SpriteAtlasElement& image, const std::array& iconOffset, const float iconRotation) { + float dx = iconOffset[0]; + float dy = iconOffset[1]; float x1 = dx - image.spriteImage->getWidth() / 2.0f; float x2 = x1 + image.spriteImage->getWidth(); float y1 = dy - image.spriteImage->getHeight() / 2.0f; float y2 = y1 + image.spriteImage->getHeight(); - return PositionedIcon(image, y1, y2, x1, x2, feature.iconRotation); + return PositionedIcon(image, y1, y2, x1, x2, iconRotation); } } // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index b0e6ae3b1d..1b7b8b2733 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -36,7 +36,6 @@ public: explicit operator bool() const { return image && (*image).pos.hasArea(); } }; -PositionedIcon shapeIcon(const SpriteAtlasElement&, - const SymbolFeature&); +PositionedIcon shapeIcon(const SpriteAtlasElement&, const std::array& iconOffset, const float iconRotation); } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index a195885415..68f48e81fd 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -15,9 +15,23 @@ class VectorTileLayer; using packed_iter_type = protozero::iterator_range; +struct VectorTileLayerData { + VectorTileLayerData(std::shared_ptr); + + // Hold a reference to the underlying pbf data that backs the lazily-built + // components of the owning VectorTileLayer and VectorTileFeature objects + std::shared_ptr data; + + uint32_t version = 1; + uint32_t extent = 4096; + std::unordered_map keysMap; + std::vector> keys; + std::vector values; +}; + class VectorTileFeature : public GeometryTileFeature { public: - VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&); + VectorTileFeature(protozero::pbf_reader, std::shared_ptr layerData); FeatureType getType() const override { return type; } optional getValue(const std::string&) const override; @@ -26,16 +40,16 @@ public: GeometryCollection getGeometries() const override; private: - const VectorTileLayer& layer; + std::shared_ptr layerData; optional id; FeatureType type = FeatureType::Unknown; packed_iter_type tags_iter; packed_iter_type geometry_iter; }; - + class VectorTileLayer : public GeometryTileLayer { public: - VectorTileLayer(protozero::pbf_reader); + VectorTileLayer(protozero::pbf_reader, std::shared_ptr); std::size_t featureCount() const override { return features.size(); } std::unique_ptr getFeature(std::size_t) const override; @@ -46,12 +60,8 @@ private: friend class VectorTileFeature; std::string name; - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map keysMap; - std::vector> keys; - std::vector values; std::vector features; + std::shared_ptr data; }; class VectorTileData : public GeometryTileData { @@ -117,8 +127,8 @@ Value parseValue(protozero::pbf_reader data) { return false; } -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const VectorTileLayer& layer_) - : layer(layer_) { +VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr layerData_) + : layerData(std::move(layerData_)) { while (feature_pbf.next()) { switch (feature_pbf.tag()) { case 1: // id @@ -141,8 +151,8 @@ VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const Ve } optional VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layer.keysMap.find(key); - if (keyIter == layer.keysMap.end()) { + auto keyIter = layerData->keysMap.find(key); + if (keyIter == layerData->keysMap.end()) { return optional(); } @@ -151,7 +161,7 @@ optional VectorTileFeature::getValue(const std::string& key) const { while (start_itr != end_itr) { uint32_t tag_key = static_cast(*start_itr++); - if (layer.keysMap.size() <= tag_key) { + if (layerData->keysMap.size() <= tag_key) { throw std::runtime_error("feature referenced out of range key"); } @@ -160,12 +170,12 @@ optional VectorTileFeature::getValue(const std::string& key) const { } uint32_t tag_val = static_cast(*start_itr++);; - if (layer.values.size() <= tag_val) { + if (layerData->values.size() <= tag_val) { throw std::runtime_error("feature referenced out of range value"); } if (tag_key == keyIter->second) { - return layer.values[tag_val]; + return layerData->values[tag_val]; } } @@ -182,7 +192,7 @@ std::unordered_map VectorTileFeature::getProperties() const { throw std::runtime_error("uneven number of feature tag ids"); } uint32_t tag_val = static_cast(*start_itr++); - properties[layer.keys.at(tag_key)] = layer.values.at(tag_val); + properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); } return properties; } @@ -196,7 +206,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { uint32_t length = 0; int32_t x = 0; int32_t y = 0; - const float scale = float(util::EXTENT) / layer.extent; + const float scale = float(util::EXTENT) / layerData->extent; GeometryCollection lines; @@ -234,7 +244,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { } } - if (layer.version >= 2 || type != FeatureType::Polygon) { + if (layerData->version >= 2 || type != FeatureType::Polygon) { return lines; } @@ -250,7 +260,7 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const parsed = true; protozero::pbf_reader tile_pbf(*data); while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message()); + VectorTileLayer layer(tile_pbf.get_message(), data); layers.emplace(layer.name, std::move(layer)); } } @@ -262,7 +272,13 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const return nullptr; } -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { +VectorTileLayerData::VectorTileLayerData(std::shared_ptr pbfData) : + data(std::move(pbfData)) +{} + +VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr pbfData) + : data(std::make_shared(std::move(pbfData))) +{ while (layer_pbf.next()) { switch (layer_pbf.tag()) { case 1: // name @@ -273,18 +289,18 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { break; case 3: // keys { - auto iter = keysMap.emplace(layer_pbf.get_string(), keysMap.size()); - keys.emplace_back(std::reference_wrapper(iter.first->first)); + auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); + data->keys.emplace_back(std::reference_wrapper(iter.first->first)); } break; case 4: // values - values.emplace_back(parseValue(layer_pbf.get_message())); + data->values.emplace_back(parseValue(layer_pbf.get_message())); break; case 5: // extent - extent = layer_pbf.get_uint32(); + data->extent = layer_pbf.get_uint32(); break; case 15: // version - version = layer_pbf.get_uint32(); + data->version = layer_pbf.get_uint32(); break; default: layer_pbf.skip(); @@ -294,7 +310,7 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { } std::unique_ptr VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique(features.at(i), *this); + return std::make_unique(features.at(i), data); } std::string VectorTileLayer::getName() const { -- cgit v1.2.1