From fa3a41136ca6345f34b53a1f211926cc1bd8649c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Tue, 29 Jul 2014 18:27:59 +0200 Subject: merge text and icon buckets/styles (we're not yet placing together!) --- bin/style.js | 100 ++++++------ include/mbgl/map/tile_parser.hpp | 11 +- include/mbgl/renderer/icon_bucket.hpp | 53 ------- include/mbgl/renderer/painter.hpp | 8 +- include/mbgl/renderer/symbol_bucket.hpp | 82 ++++++++++ include/mbgl/renderer/text_bucket.hpp | 61 ------- include/mbgl/style/property_key.hpp | 2 + include/mbgl/style/style_bucket.hpp | 56 ++----- include/mbgl/style/style_parser.hpp | 4 +- include/mbgl/style/style_properties.hpp | 32 +--- include/mbgl/style/types.hpp | 219 +++++++++++++------------- include/mbgl/text/placement.hpp | 8 +- src/map/map.cpp | 8 +- src/map/tile_parser.cpp | 119 ++------------ src/renderer/icon_bucket.cpp | 79 ---------- src/renderer/line_bucket.cpp | 2 +- src/renderer/painter_icon.cpp | 45 ------ src/renderer/painter_symbol.cpp | 202 ++++++++++++++++++++++++ src/renderer/painter_text.cpp | 156 ------------------ src/renderer/symbol_bucket.cpp | 271 ++++++++++++++++++++++++++++++++ src/renderer/text_bucket.cpp | 146 ----------------- src/style/property_fallback.cpp | 1 + src/style/style_bucket.cpp | 3 +- src/style/style_layer.cpp | 42 ++--- src/style/style_parser.cpp | 138 +++++++++------- src/style/style_properties.cpp | 2 - src/text/placement.cpp | 16 +- 27 files changed, 879 insertions(+), 987 deletions(-) delete mode 100644 include/mbgl/renderer/icon_bucket.hpp create mode 100644 include/mbgl/renderer/symbol_bucket.hpp delete mode 100644 include/mbgl/renderer/text_bucket.hpp delete mode 100644 src/renderer/icon_bucket.cpp delete mode 100644 src/renderer/painter_icon.cpp create mode 100644 src/renderer/painter_symbol.cpp delete mode 100644 src/renderer/painter_text.cpp create mode 100644 src/renderer/symbol_bucket.cpp delete mode 100644 src/renderer/text_bucket.cpp diff --git a/bin/style.js b/bin/style.js index dc2e05709a..cca10b346f 100644 --- a/bin/style.js +++ b/bin/style.js @@ -1543,10 +1543,10 @@ module.exports = { "source": "mapbox", "source-layer": "country_label", "filter": { "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 24, "text-max-width": 5 @@ -1568,10 +1568,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "LineString", "labelrank": 1 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 30, "text-max-angle": 0.5, @@ -1596,10 +1596,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "LineString", "labelrank": 2 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 24, "text-max-angle": 0.5 @@ -1623,10 +1623,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "LineString", "labelrank": 3 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 18, "text-max-angle": 0.5 @@ -1650,10 +1650,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "LineString", "labelrank": [4, 5, 6] }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 16, "text-max-angle": 0.5 @@ -1677,10 +1677,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "Point", "labelrank": 1 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 30, "text-max-width": 8, @@ -1706,10 +1706,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "Point", "labelrank": 2 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 24, "text-max-width": 8, @@ -1735,10 +1735,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "Point", "labelrank": 3 }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 18, "text-max-width": 8, @@ -1764,10 +1764,10 @@ module.exports = { "source": "mapbox", "source-layer": "marine_label", "filter": { "$type": "Point", "labelrank": [4, 5, 6] }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 16, "text-max-width": 8, @@ -1793,10 +1793,10 @@ module.exports = { "source": "mapbox", "source-layer": "state_label", "filter": { "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 16, "text-max-width": 8 @@ -1822,10 +1822,10 @@ module.exports = { "source": "mapbox", "source-layer": "place_label", "filter": { "type": "city", "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 20, "text-max-width": 8 @@ -1851,10 +1851,10 @@ module.exports = { "source": "mapbox", "source-layer": "place_label", "filter": { "type": "town", "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 24, "text-max-width": 8 @@ -1880,10 +1880,10 @@ module.exports = { "source": "mapbox", "source-layer": "place_label", "filter": { "type": "village", "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 22, "text-max-width": 8 @@ -1909,10 +1909,10 @@ module.exports = { "source": "mapbox", "source-layer": "place_label", "filter": { "type": ["hamlet", "suburb", "neighbourhood"], "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 18, "text-max-width": 6 @@ -1937,10 +1937,10 @@ module.exports = { "source": "mapbox", "source-layer": "road_label", "filter": { "class": ["motorway", "main"], "$type": "LineString" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 18, @@ -1963,10 +1963,10 @@ module.exports = { "source": "mapbox", "source-layer": "road_label", "filter": { "class": ["street", "street_limited"], "$type": "LineString" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 16, @@ -1989,10 +1989,10 @@ module.exports = { "source": "mapbox", "source-layer": "road_label", "filter": { "class": ["service", "driveway", "path"], "$type": "LineString" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 14, @@ -2015,9 +2015,9 @@ module.exports = { "source": "mapbox", "source-layer": "contour", "filter": { "index": [5, 10], "$type": "LineString" }, - "type": "text", + "type": "symbol", "render": { - "text-path": "curve", + "symbol-placement": "line", "text-field": "{ele} m", "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 10, @@ -2040,10 +2040,10 @@ module.exports = { "source": "mapbox", "source-layer": "water_label", "filter": { "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 12, "text-max-width": 8 @@ -2061,10 +2061,10 @@ module.exports = { "source": "mapbox", "source-layer": "waterway_label", "filter": { "$type": "LineString" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "curve", + "symbol-placement": "line", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 12, "text-max-angle": 0.5 @@ -2083,7 +2083,7 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": [1, 2] }, - "type": "icon", + "type": "symbol", "render": { "icon-image": "{maki}-12", "icon-size": 12 @@ -2094,10 +2094,10 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": [1, 2], "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 12, @@ -2121,7 +2121,7 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 3 }, - "type": "icon", + "type": "symbol", "render": { "icon-image": "{maki}-12", "icon-size": 12 @@ -2141,10 +2141,10 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 3, "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 11, @@ -2174,7 +2174,7 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 4 }, - "type": "icon", + "type": "symbol", "render": { "icon-image": "{maki}-12", "icon-size": 12 @@ -2194,10 +2194,10 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 4, "$type": "Point" }, - "type": "text", + "type": "symbol", "render": { "text-field": "{name_en}", - "text-path": "horizontal", + "symbol-placement": "point", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 10, @@ -2227,7 +2227,7 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "maki": "airport" }, - "type": "icon", + "type": "symbol", "render": { "icon-image": "{maki}-12", "icon-size": 12 diff --git a/include/mbgl/map/tile_parser.hpp b/include/mbgl/map/tile_parser.hpp index 1550350ed7..84505c1e05 100644 --- a/include/mbgl/map/tile_parser.hpp +++ b/include/mbgl/map/tile_parser.hpp @@ -21,9 +21,8 @@ class SpriteAtlas; class Style; class StyleBucket; class StyleBucketFill; -class StyleBucketIcon; class StyleBucketLine; -class StyleBucketText; +class StyleBucketSymbol; class StyleLayerGroup; class VectorTileData; @@ -40,18 +39,14 @@ public: private: bool obsolete() const; - void parseGlyphs(); void parseStyleLayers(std::shared_ptr group); - void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); std::unique_ptr createBucket(std::shared_ptr bucket_desc); std::unique_ptr createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill); std::unique_ptr createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line); - std::unique_ptr createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon); - std::unique_ptr createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text); + std::unique_ptr createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol); - template void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter); - template void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args); + template void addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter); private: const VectorTile vector_data; diff --git a/include/mbgl/renderer/icon_bucket.hpp b/include/mbgl/renderer/icon_bucket.hpp deleted file mode 100644 index e3b311332d..0000000000 --- a/include/mbgl/renderer/icon_bucket.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef MBGL_RENDERER_ICONBUCKET -#define MBGL_RENDERER_ICONBUCKET - -#include -#include -#include -#include - -#include -#include - -#ifndef BUFFER_OFFSET -#define BUFFER_OFFSET(i) ((char *)nullptr + (i)) -#endif - -namespace mbgl { - -class Style; -class IconVertexBuffer; -class BucketDescription; -class IconShader; -class DotShader; -class SpriteAtlas; -class VectorTileFeature; - -class IconBucket : public Bucket { -public: - IconBucket(IconVertexBuffer& vertexBuffer, - const StyleBucketIcon& properties); - - virtual void render(Painter& painter, std::shared_ptr layer_desc, const Tile::ID& id); - virtual bool hasData() const; - - void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas); - - void drawIcons(IconShader& shader); - void drawIcons(DotShader& shader); - -public: - const StyleBucketIcon &properties; - -private: - - IconVertexBuffer& vertexBuffer; - VertexArrayObject array; - - const size_t vertex_start; - size_t vertex_end = 0; -}; - -} - -#endif diff --git a/include/mbgl/renderer/painter.hpp b/include/mbgl/renderer/painter.hpp index dedb4dc120..b4023de167 100644 --- a/include/mbgl/renderer/painter.hpp +++ b/include/mbgl/renderer/painter.hpp @@ -38,8 +38,7 @@ class StyleSource; class FillBucket; class LineBucket; -class IconBucket; -class TextBucket; +class SymbolBucket; class RasterBucket; struct FillProperties; @@ -83,8 +82,7 @@ public: void renderFill(FillBucket& bucket, const FillProperties& properties, const Tile::ID& id, const mat4 &mat); void renderFill(FillBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); void renderLine(LineBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); - void renderIcon(IconBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); - void renderText(TextBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); + void renderSymbol(SymbolBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); void renderRaster(RasterBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id); void preparePrerender(PrerenderedTexture &texture); @@ -119,7 +117,7 @@ public: bool needsAnimation() const; private: void setupShaders(); - const mat4 &translatedMatrix(const std::array &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Default); + const mat4 &translatedMatrix(const std::array &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Map); void prepareTile(const Tile& tile); diff --git a/include/mbgl/renderer/symbol_bucket.hpp b/include/mbgl/renderer/symbol_bucket.hpp new file mode 100644 index 0000000000..a073856d2b --- /dev/null +++ b/include/mbgl/renderer/symbol_bucket.hpp @@ -0,0 +1,82 @@ +#ifndef MBGL_RENDERER_SYMBOLBUCKET +#define MBGL_RENDERER_SYMBOLBUCKET + +#include "bucket.hpp" +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class Style; +class TextVertexBuffer; +class IconVertexBuffer; +class TriangleElementsBuffer; +class TextShader; +class IconShader; +class DotShader; +class Placement; +class SpriteAtlas; +class GlyphAtlas; +class GlyphStore; +class FontStack; + +class SymbolBucket : public Bucket { + typedef ElementGroup triangle_group_type; + +public: + SymbolBucket( + TextVertexBuffer &textVertexBuffer, + IconVertexBuffer& iconVertexBuffer, + TriangleElementsBuffer &triangleElementsBuffer, + const StyleBucketSymbol &properties, Placement &placement); + + virtual void render(Painter &painter, std::shared_ptr layer_desc, const Tile::ID &id); + virtual bool hasData() const; + virtual bool hasTextData() const; + virtual bool hasIconData() const; + + void addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, + const Tile::ID &id, SpriteAtlas &spriteAtlas, GlyphAtlas &glyphAtlas, + GlyphStore &glyphStore); + + void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange, + float zoom); + + + void drawGlyphs(TextShader &shader); + void drawIcons(IconShader& shader); + void drawIcons(DotShader& shader); + +private: + void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, + const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); + void addFeature(const pbf &geom_pbf, const GlyphPositions &face, const Shaping &shaping); + +public: + const StyleBucketSymbol &properties; + +private: + TextVertexBuffer& textVertexBuffer; + IconVertexBuffer& iconVertexBuffer; + TriangleElementsBuffer& triangleElementsBuffer; + Placement &placement; + + const size_t text_vertex_start; + const size_t icon_vertex_start; + const size_t triangle_elements_start; + size_t icon_vertex_end = 0; + VertexArrayObject array; + + std::vector triangleGroups; +}; +} + +#endif diff --git a/include/mbgl/renderer/text_bucket.hpp b/include/mbgl/renderer/text_bucket.hpp deleted file mode 100644 index cb4b8f2cda..0000000000 --- a/include/mbgl/renderer/text_bucket.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef MBGL_RENDERER_TEXTBUCKET -#define MBGL_RENDERER_TEXTBUCKET - -#include "bucket.hpp" -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace mbgl { - -class Style; -class TextVertexBuffer; -class TriangleElementsBuffer; -class TextShader; -class Placement; -struct pbf; - -class TextBucket : public Bucket { - typedef ElementGroup triangle_group_type; - -public: - TextBucket( - TextVertexBuffer &vertexBuffer, - TriangleElementsBuffer &triangleElementsBuffer, - const StyleBucketText &properties, Placement &placement); - - virtual void render(Painter &painter, std::shared_ptr layer_desc, const Tile::ID &id); - virtual bool hasData() const; - - void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, - PlacementRange placementRange, float zoom); - - void addFeature(const pbf &geometry, - const GlyphPositions &face, - const Shaping &shaping); - - void drawGlyphs(TextShader &shader); - -public: - const StyleBucketText &properties; - -private: - TextVertexBuffer& vertexBuffer; - TriangleElementsBuffer& triangleElementsBuffer; - Placement &placement; - - const size_t vertex_start; - const size_t triangle_elements_start; - - std::vector triangleGroups; -}; -} - -#endif diff --git a/include/mbgl/style/property_key.hpp b/include/mbgl/style/property_key.hpp index d5553ebf0d..68dc709597 100644 --- a/include/mbgl/style/property_key.hpp +++ b/include/mbgl/style/property_key.hpp @@ -35,6 +35,7 @@ enum class PropertyKey { IconHaloColor, IconHaloWidth, IconHaloBlur, + IconTranslate, // for transitions only IconTranslateX, IconTranslateY, IconTranslateAnchor, @@ -45,6 +46,7 @@ enum class PropertyKey { TextHaloColor, TextHaloWidth, TextHaloBlur, + TextTranslate, // for transitions only TextTranslateX, TextTranslateY, TextTranslateAnchor, diff --git a/include/mbgl/style/style_bucket.hpp b/include/mbgl/style/style_bucket.hpp index 6948a96158..af76650e56 100644 --- a/include/mbgl/style/style_bucket.hpp +++ b/include/mbgl/style/style_bucket.hpp @@ -18,52 +18,27 @@ class Source; class StyleBucketFill { public: - WindingType winding = WindingType::Default; + WindingType winding = WindingType::NonZero; }; class StyleBucketLine { public: - CapType cap = CapType::Default; - JoinType join = JoinType::Default; + CapType cap = CapType::Butt; + JoinType join = JoinType::Miter; float miter_limit = 2.0f; float round_limit = 1.0f; }; -class StyleBucketIcon { +class StyleBucketSymbol { public: - uint16_t size = 16; - vec2 translate {0, 0}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Default; - std::string icon; - float spacing = 0.0f; - float padding = 2.0f; -}; - -class StyleBucketText { -public: - std::string field; - TextPathType path = TextPathType::Default; - TextTransformType transform = TextTransformType::Default; - std::string font; - float max_size = 16.0f; - float max_width = 15.0f * 24; - float line_height = 1.2f * 24; - float letter_spacing = 0.0f; - float alignment = 0.5f; - float vertical_alignment = 0.5; - vec2 translate {0, 0}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Default; - float max_angle_delta = 45.0f; - float min_distance = 250.0f; - float rotate = 0.0f; // what is this? - float padding = 2.0f; - float slant = 0.0f; - bool always_visible = false; -}; - -class StyleBucketSymbol : util::noncopyable { -public: - PlacementType placement = PlacementType::Default; + // Make movable only. + inline StyleBucketSymbol() = default; + inline StyleBucketSymbol(StyleBucketSymbol &&) = default; + inline StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default; + inline StyleBucketSymbol(const StyleBucketSymbol &) = delete; + inline StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete; + + PlacementType placement = PlacementType::Point; float min_distance = 250.0f; struct { @@ -76,6 +51,7 @@ public: float padding = 2.0f; bool keep_upright = false; vec2 offset = {0, 0}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; } icon; struct { @@ -96,6 +72,7 @@ public: bool keep_upright = true; TextTransformType transform = TextTransformType::None; vec2 offset = {0, 0}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; bool allow_overlap = false; bool ignore_placement = false; bool optional = false; @@ -106,9 +83,8 @@ class StyleBucketRaster { public: }; -typedef util::variant StyleBucketRender; +typedef util::variant StyleBucketRender; class StyleBucket { diff --git a/include/mbgl/style/style_parser.hpp b/include/mbgl/style/style_parser.hpp index 431e3af8db..8b8dc74f21 100644 --- a/include/mbgl/style/style_parser.hpp +++ b/include/mbgl/style/style_parser.hpp @@ -62,8 +62,8 @@ private: // Parses optional properties into a render bucket. template bool parseRenderProperty(JSVal value, T &target, const char *name); - template - bool parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser); + template + bool parseRenderProperty(JSVal value, T &target, const char *name); // Parses optional properties into style class properties. template diff --git a/include/mbgl/style/style_properties.hpp b/include/mbgl/style/style_properties.hpp index 9575347be3..35ea97781b 100644 --- a/include/mbgl/style/style_properties.hpp +++ b/include/mbgl/style/style_properties.hpp @@ -19,7 +19,7 @@ struct FillProperties { Color fill_color = {{ 0, 0, 0, 1 }}; Color stroke_color = {{ 0, 0, 0, -1 }}; std::array translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Default; + TranslateAnchorType translateAnchor = TranslateAnchorType::Map; std::string image; inline bool isVisible() const { @@ -32,7 +32,7 @@ struct LineProperties { float opacity = 1.0f; Color color = {{ 0, 0, 0, 1 }}; std::array translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Default; + TranslateAnchorType translateAnchor = TranslateAnchorType::Map; float width = 1; float offset = 0; float blur = 0; @@ -44,31 +44,6 @@ struct LineProperties { } }; -struct IconProperties { - inline IconProperties() {} - float opacity = 1.0f; - float rotate = 0.0f; - RotateAnchorType rotate_anchor = RotateAnchorType::Default; - - inline bool isVisible() const { - return opacity > 0; - } -}; - -struct TextProperties { - inline TextProperties() {} - float opacity = 1.0f; - float size = 12.0f; - Color color = {{ 0, 0, 0, 1 }}; - Color halo_color = {{ 1, 1, 1, 0.75 }}; - float halo_width = 0.25f; - float halo_blur = 1.0f; - - inline bool isVisible() const { - return opacity > 0 && (color[3] > 0 || halo_color[3] > 0) && size > 0; - } -}; - struct SymbolProperties { inline SymbolProperties() {} @@ -133,8 +108,7 @@ struct BackgroundProperties { typedef util::variant< FillProperties, LineProperties, - IconProperties, - TextProperties, + SymbolProperties, CompositeProperties, RasterProperties, BackgroundProperties, diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 70090a7e36..55b7685fd3 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -11,12 +11,13 @@ namespace mbgl { // Stores a premultiplied color, with all four channels ranging from 0..1 typedef std::array Color; +// ------------------------------------------------------------------------------------------------- + enum class StyleLayerType : uint8_t { Unknown, Fill, Line, - Icon, - Text, + Symbol, Raster, Composite, Background @@ -26,170 +27,172 @@ MBGL_DEFINE_ENUM_CLASS(StyleLayerTypeClass, StyleLayerType, { { StyleLayerType::Unknown, "unknown" }, { StyleLayerType::Fill, "fill" }, { StyleLayerType::Line, "line" }, - { StyleLayerType::Icon, "icon" }, - { StyleLayerType::Text, "text" }, + { StyleLayerType::Symbol, "symbol" }, { StyleLayerType::Raster, "raster" }, { StyleLayerType::Composite, "composite" }, { StyleLayerType::Background, "background" }, { StyleLayerType(-1), "unknown" }, }); +// ------------------------------------------------------------------------------------------------- -enum class WindingType : uint8_t { +enum class SourceType : uint8_t { + Vector, + Raster, + GeoJSON, + Video +}; + +MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, { + { SourceType::Vector, "vector" }, + { SourceType::Raster, "raster" }, + { SourceType::GeoJSON, "geojson" }, + { SourceType::Video, "video" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class WindingType : bool { EvenOdd, NonZero, - Default = NonZero }; +MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, { + { WindingType::EvenOdd, "even-odd" }, + { WindingType::NonZero, "non-zero" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class CapType : uint8_t { Round, Butt, Square, - Default = Butt }; +MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, { + { CapType::Round, "round" }, + { CapType::Butt, "butt" }, + { CapType::Square, "square" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class JoinType : uint8_t { Miter, Bevel, - Round, - Default = Miter + Round }; -enum class TextPathType : uint8_t { - Horizontal, - Curve, - Default = Horizontal -}; +MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, { + { JoinType::Miter, "miter" }, + { JoinType::Bevel, "bevel" }, + { JoinType::Round, "round" }, +}); -enum class PlacementType : bool { - Point, - Line, - Default = Point -}; +// ------------------------------------------------------------------------------------------------- -enum class TextTransformType : uint8_t { - None, - Uppercase, - Lowercase, - Default = None +enum class TranslateAnchorType : bool { + Map, + Viewport }; -enum class TranslateAnchorType : uint8_t { +MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, { + { TranslateAnchorType::Map, "map" }, + { TranslateAnchorType::Viewport, "viewport" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class RotateAnchorType : bool { Map, Viewport, - Default = Map }; -enum class RotateAnchorType : uint8_t { - Map, - Viewport, - Default = Viewport +MBGL_DEFINE_ENUM_CLASS(RotateAnchorTypeClass, RotateAnchorType, { + { RotateAnchorType::Map, "map" }, + { RotateAnchorType::Viewport, "viewport" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class PlacementType : bool { + Point, + Line, }; +MBGL_DEFINE_ENUM_CLASS(PlacementTypeClass, PlacementType, { + { PlacementType::Point, "point" }, + { PlacementType::Line, "line" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class RotationAlignmentType : bool { Map, Viewport, - Default = Viewport }; +MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, { + { RotationAlignmentType::Map, "map" }, + { RotationAlignmentType::Viewport, "viewport" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class TextJustifyType : uint8_t { Center, Left, - Right, - Default = Center + Right }; +MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, { + { TextJustifyType::Center, "center" }, + { TextJustifyType::Left, "left" }, + { TextJustifyType::Right, "right" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class TextHorizontalAlignType : uint8_t { Left, Center, Right, - Default = Center }; +MBGL_DEFINE_ENUM_CLASS(TextHorizontalAlignTypeClass, TextHorizontalAlignType, { + { TextHorizontalAlignType::Left, "left" }, + { TextHorizontalAlignType::Center, "center" }, + { TextHorizontalAlignType::Right, "right" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class TextVerticalAlignType : uint8_t { Top, Center, Bottom, - Default = Center -}; - -enum class SourceType : uint8_t { - Vector, - Raster, - GeoJSON, - Video, - Default = Vector }; -inline WindingType parseWindingType(const std::string &type) { - if (type == "even-odd") return WindingType::EvenOdd; - if (type == "non-zero") return WindingType::NonZero; - return WindingType::Default; -} - -inline CapType parseCapType(const std::string &cap) { - if (cap == "round") return CapType::Round; - if (cap == "butt") return CapType::Butt; - if (cap == "square") return CapType::Square; - return CapType::Default; -} - -inline JoinType parseJoinType(const std::string &join) { - if (join == "miter") return JoinType::Miter; - if (join == "bevel") return JoinType::Bevel; - if (join == "round") return JoinType::Round; - return JoinType::Default; -} - -inline TextPathType parseTextPathType(const std::string &path) { - if (path == "horizontal") return TextPathType::Horizontal; - if (path == "curve") return TextPathType::Curve; - return TextPathType::Default; -} +MBGL_DEFINE_ENUM_CLASS(TextVerticalAlignTypeClass, TextVerticalAlignType, { + { TextVerticalAlignType::Top, "top" }, + { TextVerticalAlignType::Center, "center" }, + { TextVerticalAlignType::Bottom, "bottom" }, +}); -inline PlacementType parsePlacementType(const std::string &path) { - if (path == "point") return PlacementType::Point; - if (path == "line") return PlacementType::Line; - return PlacementType::Default; -} +// ------------------------------------------------------------------------------------------------- -inline TextTransformType parseTextTransformType(const std::string& transform) { - if (transform == "uppercase") return TextTransformType::Uppercase; - if (transform == "lowercase") return TextTransformType::Lowercase; - return TextTransformType::Default; +enum class TextTransformType : uint8_t { + None, + Uppercase, + Lowercase, }; -inline TranslateAnchorType parseTranslateAnchorType(const std::string &anchor) { - if (anchor == "map") return TranslateAnchorType::Map; - if (anchor == "viewport") return TranslateAnchorType::Viewport; - return TranslateAnchorType::Default; -} - -inline RotateAnchorType parseRotateAnchorType(const std::string &anchor) { - if (anchor == "map") return RotateAnchorType::Map; - if (anchor == "viewport") return RotateAnchorType::Viewport; - return RotateAnchorType::Default; -} - -inline float parseAlignmentType(const std::string &alignment) { - if (alignment == "right") return 1.0f; - if (alignment == "left") return 0.0f; - return 0.5f; -} - -inline float parseVerticalAlignmentType(const std::string &alignment) { - if (alignment == "bottom") return 1.0f; - if (alignment == "top") return 0.0f; - return 0.5f; -} - -inline SourceType parseSourceType(const std::string &source) { - if (source == "vector") return SourceType::Vector; - if (source == "raster") return SourceType::Raster; - if (source == "geojson") return SourceType::GeoJSON; - if (source == "video") return SourceType::Video; - return SourceType::Default; -} +MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, { + { TextTransformType::None, "none" }, + { TextTransformType::Uppercase, "uppercase" }, + { TextTransformType::Lowercase, "lowercase" }, +}); } diff --git a/include/mbgl/text/placement.hpp b/include/mbgl/text/placement.hpp index aa03712e30..7700f32f0a 100644 --- a/include/mbgl/text/placement.hpp +++ b/include/mbgl/text/placement.hpp @@ -9,15 +9,15 @@ namespace mbgl { -class TextBucket; -class StyleBucketText; +class SymbolBucket; +class StyleBucketSymbol; class Placement { public: Placement(int8_t zoom, float placementDepth); - void addFeature(TextBucket &bucket, const std::vector &line, - const StyleBucketText &info, + void addFeature(SymbolBucket &bucket, const std::vector &line, + const StyleBucketSymbol &info, const GlyphPositions &face, const Shaping &shaping); diff --git a/src/map/map.cpp b/src/map/map.cpp index 9252a57cce..ca43662e09 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -683,13 +683,9 @@ void Map::renderLayer(std::shared_ptr layer_desc, RenderPass pass) { if (pass == Opaque) return; if (!layer_desc->getProperties().isVisible()) return; break; - case StyleLayerType::Icon: + case StyleLayerType::Symbol: if (pass == Opaque) return; - if (!layer_desc->getProperties().isVisible()) return; - break; - case StyleLayerType::Text: - if (pass == Opaque) return; - if (!layer_desc->getProperties().isVisible()) return; + if (!layer_desc->getProperties().isVisible()) return; break; case StyleLayerType::Raster: if (pass == Translucent) return; diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 807e827972..6be9bcb32c 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -6,8 +6,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -51,21 +50,6 @@ void TileParser::parse() { bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; } -void TileParser::addGlyph(uint64_t tileid, const std::string stackname, - const std::u32string &string, const FontStack &fontStack, - GlyphAtlas &glyphAtlas, GlyphPositions &face) { - const std::map &sdfs = fontStack.getSDFs(); - // Loop through all characters and add glyph to atlas, positions. - for (uint32_t chr : string) { - auto sdf_it = sdfs.find(chr); - if (sdf_it != sdfs.end()) { - const SDFGlyph& sdf = sdf_it->second; - const Rect rect = glyphAtlas.addGlyph(tileid, stackname, sdf); - face.emplace(chr, Glyph{rect, sdf.metrics}); - } - } -} - void TileParser::parseStyleLayers(std::shared_ptr group) { if (!group) { return; @@ -119,10 +103,8 @@ std::unique_ptr TileParser::createBucket(std::shared_ptr bu return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get()); } else if (bucket_desc->render.is()) { return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { - return createIconBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { - return createTextBucket(layer, bucket_desc->filter, bucket_desc->render.get()); + } else if (bucket_desc->render.is()) { + return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get()); } else if (bucket_desc->render.is()) { return nullptr; } else { @@ -140,7 +122,7 @@ std::unique_ptr TileParser::createBucket(std::shared_ptr bu } template -void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) { +void TileParser::addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) { FilteredVectorTileLayer filtered_layer(layer, filter); for (pbf feature : filtered_layer) { if (obsolete()) @@ -157,103 +139,20 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, } } -template -void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args) { - FilteredVectorTileLayer filtered_layer(layer, filter); - for (const pbf &feature_pbf : filtered_layer) { - if (obsolete()) { - return; - } - bucket->addFeature({feature_pbf, layer}, std::forward(args)...); - } -} - - std::unique_ptr TileParser::createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill) { std::unique_ptr bucket = std::make_unique(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, fill); - addBucketFeatures(bucket, layer, filter); + addBucketGeometries(bucket, layer, filter); return obsolete() ? nullptr : std::move(bucket); } std::unique_ptr TileParser::createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line) { std::unique_ptr bucket = std::make_unique(tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, line); - addBucketFeatures(bucket, layer, filter); + addBucketGeometries(bucket, layer, filter); return obsolete() ? nullptr : std::move(bucket); } -std::unique_ptr TileParser::createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon) { - std::unique_ptr bucket = std::make_unique(tile.iconVertexBuffer, icon); - addBucketFeatures(bucket, layer, filter, *spriteAtlas); +std::unique_ptr TileParser::createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol) { + std::unique_ptr bucket = std::make_unique(tile.textVertexBuffer, tile.iconVertexBuffer, tile.triangleElementsBuffer, symbol, placement); + bucket->addFeatures(layer, filter, tile.id, *spriteAtlas, *glyphAtlas, *glyphStore); return obsolete() ? nullptr : std::move(bucket); } - -std::unique_ptr TileParser::createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text) { - - // Make sure that we always have a valid glyph store. If this is not set, the stylesheet - // doesn't specify a glyph URL. - if (!glyphStore) { - return nullptr; - } - - const StyleBucketText &properties = text; - - std::unique_ptr bucket = std::make_unique( - tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement); - - util::utf8_to_utf32 ucs4conv; - - std::vector> labels; - - // Determine and load glyph ranges - { - std::set ranges; - - FilteredVectorTileLayer filtered_layer(layer, filter); - for (const pbf &feature_pbf : filtered_layer) { - if (obsolete()) - return nullptr; - VectorTileFeature feature{feature_pbf, layer}; - - std::string u8string = util::replaceTokens(properties.field, feature.properties); - - auto& convert = std::use_facet>(std::locale()); - if (properties.transform == TextTransformType::Uppercase) { - convert.toupper(&u8string[0], &u8string[0] + u8string.size()); - } else if (properties.transform == TextTransformType::Lowercase) { - convert.tolower(&u8string[0], &u8string[0] + u8string.size()); - } - - std::u32string string = ucs4conv.convert(u8string); - - // Loop through all characters of this text and collect unique codepoints. - for (char32_t chr : string) { - ranges.insert(getGlyphRange(chr)); - } - - labels.emplace_back(string, feature.geometry); - } - - glyphStore->waitForGlyphRanges(properties.font, ranges); - } - - // Create a copy! - const FontStack &fontStack = glyphStore->getFontStack(properties.font); - GlyphPositions face; - - // Shape and place all labels. - for (const std::pair &label : labels) { - - // Shape labels. - const Shaping shaping = fontStack.getShaping(label.first, properties.max_width, - properties.line_height, properties.alignment, - properties.vertical_alignment, properties.letter_spacing); - - // Place labels. - addGlyph(tile.id.to_uint64(), properties.font, label.first, fontStack, *glyphAtlas, - face); - - bucket->addFeature(label.second, face, shaping); - } - - return std::move(bucket); -} diff --git a/src/renderer/icon_bucket.cpp b/src/renderer/icon_bucket.cpp deleted file mode 100644 index af988fee80..0000000000 --- a/src/renderer/icon_bucket.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - -struct geometry_too_long_exception : std::exception {}; - -using namespace mbgl; - -IconBucket::IconBucket(IconVertexBuffer& vertexBuffer, - const StyleBucketIcon& properties) - : properties(properties), - vertexBuffer(vertexBuffer), - vertex_start(vertexBuffer.index()) { -} - -void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) { - std::string field; - - if (properties.icon.size()) { - field = util::replaceTokens(properties.icon, feature.properties); - } - - if (!field.size()) { - field = ""; - } - - const Rect rect = sprite_atlas.getIcon(properties.size, field); - const uint16_t tx = rect.x + rect.w / 2; - const uint16_t ty = rect.y + rect.h / 2; - - Geometry::command cmd; - pbf geom = feature.geometry; - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - vertexBuffer.add(x, y, tx, ty); - } else { - if (debug::tileParseWarnings) { - fprintf(stderr, "[WARNING] other command than move_to in icon geometry\n"); - } - } - } - - vertex_end = vertexBuffer.index(); -} - -void IconBucket::render(Painter& painter, std::shared_ptr layer_desc, const Tile::ID& id) { - painter.renderIcon(*this, layer_desc, id); -} - -bool IconBucket::hasData() const { - return vertex_end > 0; -} - -void IconBucket::drawIcons(IconShader& shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - array.bind(shader, vertexBuffer, vertex_index); - glDrawArrays(GL_POINTS, 0, (GLsizei)(vertex_end - vertex_start)); -} - -void IconBucket::drawIcons(DotShader& shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - array.bind(shader, vertexBuffer, vertex_index); - glDrawArrays(GL_POINTS, 0, (GLsizei)(vertex_end - vertex_start)); -} diff --git a/src/renderer/line_bucket.cpp b/src/renderer/line_bucket.cpp index 74cfba3c6d..2e04820e42 100644 --- a/src/renderer/line_bucket.cpp +++ b/src/renderer/line_bucket.cpp @@ -80,7 +80,7 @@ void LineBucket::addGeometry(const std::vector& vertices) { CapType beginCap = properties.cap; CapType endCap = closed ? CapType::Butt : properties.cap; - JoinType currentJoin = JoinType::Default; + JoinType currentJoin = JoinType::Miter; Coordinate currentVertex = Coordinate::null(), prevVertex = Coordinate::null(), diff --git a/src/renderer/painter_icon.cpp b/src/renderer/painter_icon.cpp deleted file mode 100644 index 158030df19..0000000000 --- a/src/renderer/painter_icon.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -using namespace mbgl; - -void Painter::renderIcon(IconBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id) { - // Abort early. - if (!bucket.hasData()) return; - if (pass == Opaque) return; - - const IconProperties &properties = layer_desc->getProperties(); - -// TODO: when translating icon, are we doing this in the bucket already? -// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); - - SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); - - useProgram(iconShader->program); - iconShader->setMatrix(matrix); -// TODO: update - iconShader->setColor({{ 1, 1, 1, 1 }}); - iconShader->setImage(0); - iconShader->setRatio(map.getState().getPixelRatio()); - iconShader->setDimension({{ - spriteAtlas.getTextureWidth(), - spriteAtlas.getTextureHeight(), - }}); - - spriteAtlas.bind(map.getState().isChanging()); - - const float iconSize = bucket.properties.size * map.getState().getPixelRatio(); - iconShader->setSize(iconSize); -#ifndef GL_ES_VERSION_2_0 - glPointSize(iconSize); - glEnable(GL_POINT_SPRITE); -#endif - - glDepthRange(strata, 1.0f); - bucket.drawIcons(*iconShader); -} diff --git a/src/renderer/painter_symbol.cpp b/src/renderer/painter_symbol.cpp new file mode 100644 index 0000000000..8201e4b7f1 --- /dev/null +++ b/src/renderer/painter_symbol.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr layer_desc, + const Tile::ID &id) { + // Abort early. + if (pass == Opaque) { + return; + } + + if (bucket.hasTextData()) { + const SymbolProperties &properties = layer_desc->getProperties(); + + mat4 exMatrix; + matrix::copy(exMatrix, projMatrix); + if (bucket.properties.placement == PlacementType::Line) { + matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle()); + } + + // If layerStyle.size > bucket.info.fontSize then labels may collide + float fontSize = std::fmin(properties.text.size, bucket.properties.text.max_size); + matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f); + + // TODO: figure out whether we actually need to account for this while painting; we might + // already have + // done this during label placement. + // const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, + // properties.translateAnchor); + + useProgram(textShader->program); + textShader->setMatrix(matrix); + textShader->setExtrudeMatrix(exMatrix); + + GlyphAtlas &glyphAtlas = *map.getGlyphAtlas(); + glyphAtlas.bind(); + textShader->setTextureSize( + {{static_cast(glyphAtlas.width), static_cast(glyphAtlas.height)}}); + + // Convert the -pi..pi to an int8 range. + float angle = std::round((map.getState().getAngle()) / M_PI * 128); + + // adjust min/max zooms for variable font sies + float zoomAdjust = log(fontSize / bucket.properties.text.max_size) / log(2); + + textShader->setAngle((int32_t)(angle + 256) % 256); + textShader->setFlip(bucket.properties.placement == PlacementType::Line ? 1 : 0); + textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * + 10); // current zoom level + + // Label fading + const timestamp duration = 300_milliseconds; + const timestamp currentTime = util::now(); + + std::deque &history = frameHistory.history; + + // Remove frames until only one is outside the duration, or until there are only three + while (history.size() > 3 && history[1].t + duration < currentTime) { + history.pop_front(); + } + + if (history[1].t + duration < currentTime) { + history[0].z = history[1].z; + } + + assert("there should never be less than three frames in the history" && + (history.size() >= 3)); + + // Find the range of zoom levels we want to fade between + float startingZ = history.front().z; + const FrameSnapshot lastFrame = history.back(); + float endingZ = lastFrame.z; + float lowZ = std::fmin(startingZ, endingZ); + float highZ = std::fmax(startingZ, endingZ); + + // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one + // duration + float zoomDiff = endingZ - history[1].z, timeDiff = lastFrame.t - history[1].t; + float fadedist = zoomDiff / (timeDiff / duration); + +#if defined(DEBUG) + if (std::isnan(fadedist)) + fprintf(stderr, "fadedist should never be NaN\n"); +#endif + + // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed + // bump is how much farther it would have been if it had continued zooming at the same rate + float bump = (currentTime - lastFrame.t) / duration * fadedist; + + textShader->setFadeDist(fadedist * 10); + textShader->setMinFadeZoom(std::floor(lowZ * 10)); + textShader->setMaxFadeZoom(std::floor(highZ * 10)); + textShader->setFadeZoom((map.getState().getNormalizedZoom() + bump) * 10); + + // This defines the gamma around the SDF cutoff value. + const float sdfGamma = 0.75f / 10.0f; + + // Our signed distance fields are scaled so that 1 pixel is scaled to 32 pixels. + // Our cutoff between positive and negative values is hard coded to 64 (== 2 pixels). + // This means that our 6/8 of the value range lies outside the glyph outline. + const float sdfOffset = (256.0f - 64.0f) / 32.0f; + + // Currently, all of our fonts are rendered with a font size of 24px. + const float sdfFontSize = 24.0f; + + // 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 gamma = sdfGamma * sdfFontSize / fontSize / map.getState().getPixelRatio(); + + // We're drawing in the translucent pass which is bottom-to-top, so we need + // to draw the halo first. + if (properties.text.halo_color[3] > 0.0f) { + const float haloWidth = util::clamp( + (sdfOffset - properties.text.halo_width / (fontSize / sdfFontSize)) / 8.0f, 0.0f, + 1.0f); + + if (properties.text.halo_blur != 0.0f) { + // We are converting the halo_blur value to current screen pixels. + // Then we're dividing it by two because the gamma value is added/subtracted into + // both + // directions in the shader, but the halo_blur describes the entire width of the + // blur. + // Note that this does *not* have to be adjusted for retina screens, because we want + // the + // same blur width when we explicitly specify one. + textShader->setGamma((properties.text.halo_blur / (fontSize / sdfFontSize)) / 8.0f / + 2.0f); + } else { + textShader->setGamma(sdfGamma); + } + + if (properties.text.opacity < 1.0f) { + Color color = properties.text.halo_color; + color[0] *= properties.text.opacity; + color[1] *= properties.text.opacity; + color[2] *= properties.text.opacity; + color[3] *= properties.text.opacity; + textShader->setColor(color); + } else { + textShader->setColor(properties.text.halo_color); + } + textShader->setBuffer(haloWidth); + glDepthRange(strata, 1.0f); + bucket.drawGlyphs(*textShader); + } + + if (properties.text.color[3] > 0.0f) { + // Then, we draw the text over the halo + textShader->setGamma(gamma); + if (properties.text.opacity < 1.0f) { + Color color = properties.text.color; + color[0] *= properties.text.opacity; + color[1] *= properties.text.opacity; + color[2] *= properties.text.opacity; + color[3] *= properties.text.opacity; + textShader->setColor(color); + } else { + textShader->setColor(properties.text.color); + } + textShader->setBuffer((256.0f - 64.0f) / 256.0f); + glDepthRange(strata + strata_epsilon, 1.0f); + bucket.drawGlyphs(*textShader); + } + } + + if (bucket.hasIconData()) { + SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); + + useProgram(iconShader->program); + iconShader->setMatrix(matrix); + + // TODO: update + iconShader->setColor({{1, 1, 1, 1}}); + iconShader->setImage(0); + iconShader->setRatio(map.getState().getPixelRatio()); + iconShader->setDimension({{ + spriteAtlas.getTextureWidth(), spriteAtlas.getTextureHeight(), + }}); + + spriteAtlas.bind(map.getState().isChanging()); + + // TODO: remove hardcoded icon size. + const float iconSize = 12 * map.getState().getPixelRatio(); + iconShader->setSize(iconSize); +#ifndef GL_ES_VERSION_2_0 + glPointSize(iconSize); + glEnable(GL_POINT_SPRITE); +#endif + + glDepthRange(strata, 1.0f); + bucket.drawIcons(*iconShader); + } +} +} diff --git a/src/renderer/painter_text.cpp b/src/renderer/painter_text.cpp deleted file mode 100644 index d5ad3db51f..0000000000 --- a/src/renderer/painter_text.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -using namespace mbgl; - -void Painter::renderText(TextBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id) { - // Abort early. - if (pass == Opaque) return; - if (!bucket.hasData()) return; - - const TextProperties &properties = layer_desc->getProperties(); - - mat4 exMatrix; - matrix::copy(exMatrix, projMatrix); - if (bucket.properties.path == TextPathType::Curve) { - matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle()); - } - - // If layerStyle.size > bucket.info.fontSize then labels may collide - float fontSize = std::fmin(properties.size, bucket.properties.max_size); - matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f); - -// TODO: figure out whether we actually need to account for this while painting; we might already have -// done this during label placement. -// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); - - useProgram(textShader->program); - textShader->setMatrix(matrix); - textShader->setExtrudeMatrix(exMatrix); - - GlyphAtlas &glyphAtlas = *map.getGlyphAtlas(); - glyphAtlas.bind(); - textShader->setTextureSize({{static_cast(glyphAtlas.width), - static_cast(glyphAtlas.height)}}); - - // Convert the -pi..pi to an int8 range. - float angle = std::round((map.getState().getAngle()) / M_PI * 128); - - // adjust min/max zooms for variable font sies - float zoomAdjust = log(fontSize / bucket.properties.max_size) / log(2); - - textShader->setAngle((int32_t)(angle + 256) % 256); - textShader->setFlip(bucket.properties.path == TextPathType::Curve ? 1 : 0); - textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level - - // Label fading - const timestamp duration = 300_milliseconds; - const timestamp currentTime = util::now(); - - std::deque &history = frameHistory.history; - - // Remove frames until only one is outside the duration, or until there are only three - while (history.size() > 3 && history[1].t + duration < currentTime) { - history.pop_front(); - } - - if (history[1].t + duration < currentTime) { - history[0].z = history[1].z; - } - - assert("there should never be less than three frames in the history" && (history.size() >= 3)); - - // Find the range of zoom levels we want to fade between - float startingZ = history.front().z; - const FrameSnapshot lastFrame = history.back(); - float endingZ = lastFrame.z; - float lowZ = std::fmin(startingZ, endingZ); - float highZ = std::fmax(startingZ, endingZ); - - // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration - float zoomDiff = endingZ - history[1].z, - timeDiff = lastFrame.t - history[1].t; - float fadedist = zoomDiff / (timeDiff / duration); - -#if defined(DEBUG) - if (std::isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN\n"); -#endif - - // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed - // bump is how much farther it would have been if it had continued zooming at the same rate - float bump = (currentTime - lastFrame.t) / duration * fadedist; - - textShader->setFadeDist(fadedist * 10); - textShader->setMinFadeZoom(std::floor(lowZ * 10)); - textShader->setMaxFadeZoom(std::floor(highZ * 10)); - textShader->setFadeZoom((map.getState().getNormalizedZoom() + bump) * 10); - - // This defines the gamma around the SDF cutoff value. - const float sdfGamma = 0.75f / 10.0f; - - // Our signed distance fields are scaled so that 1 pixel is scaled to 32 pixels. - // Our cutoff between positive and negative values is hard coded to 64 (== 2 pixels). - // This means that our 6/8 of the value range lies outside the glyph outline. - const float sdfOffset = (256.0f - 64.0f) / 32.0f; - - // Currently, all of our fonts are rendered with a font size of 24px. - const float sdfFontSize = 24.0f; - - // 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 gamma = sdfGamma * sdfFontSize / fontSize / map.getState().getPixelRatio(); - - // We're drawing in the translucent pass which is bottom-to-top, so we need - // to draw the halo first. - if (properties.halo_color[3] > 0.0f) { - const float haloWidth = util::clamp((sdfOffset - properties.halo_width / (fontSize / sdfFontSize)) / 8.0f, 0.0f, 1.0f); - - if (properties.halo_blur != 0.0f) { - // We are converting the halo_blur value to current screen pixels. - // Then we're dividing it by two because the gamma value is added/subtracted into both - // directions in the shader, but the halo_blur describes the entire width of the blur. - // Note that this does *not* have to be adjusted for retina screens, because we want the - // same blur width when we explicitly specify one. - textShader->setGamma((properties.halo_blur / (fontSize / sdfFontSize)) / 8.0f / 2.0f); - } else { - textShader->setGamma(sdfGamma); - } - - if (properties.opacity < 1.0f) { - Color color = properties.halo_color; - color[0] *= properties.opacity; - color[1] *= properties.opacity; - color[2] *= properties.opacity; - color[3] *= properties.opacity; - textShader->setColor(color); - } else { - textShader->setColor(properties.halo_color); - } - textShader->setBuffer(haloWidth); - glDepthRange(strata, 1.0f); - bucket.drawGlyphs(*textShader); - } - - if (properties.color[3] > 0.0f) { - // Then, we draw the text over the halo - textShader->setGamma(gamma); - if (properties.opacity < 1.0f) { - Color color = properties.color; - color[0] *= properties.opacity; - color[1] *= properties.opacity; - color[2] *= properties.opacity; - color[3] *= properties.opacity; - textShader->setColor(color); - } else { - textShader->setColor(properties.color); - } - textShader->setBuffer((256.0f - 64.0f) / 256.0f); - glDepthRange(strata + strata_epsilon, 1.0f); - bucket.drawGlyphs(*textShader); - } -} diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp new file mode 100644 index 0000000000..60fd6e240a --- /dev/null +++ b/src/renderer/symbol_bucket.cpp @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +namespace mbgl { + +SymbolBucket::SymbolBucket(TextVertexBuffer &textVertexBuffer, IconVertexBuffer &iconVertexBuffer, + TriangleElementsBuffer &triangleElementsBuffer, + const StyleBucketSymbol &properties, Placement &placement) + : properties(properties), + textVertexBuffer(textVertexBuffer), + iconVertexBuffer(iconVertexBuffer), + triangleElementsBuffer(triangleElementsBuffer), + placement(placement), + text_vertex_start(textVertexBuffer.index()), + icon_vertex_start(iconVertexBuffer.index()), + triangle_elements_start(triangleElementsBuffer.index()) {} + +void SymbolBucket::render(Painter &painter, std::shared_ptr layer_desc, + const Tile::ID &id) { + painter.renderSymbol(*this, layer_desc, id); +} + +bool SymbolBucket::hasData() const { + return hasTextData() || hasIconData(); +} + + +bool SymbolBucket::hasTextData() const { + return !triangleGroups.empty(); +} + +bool SymbolBucket::hasIconData() const { + return icon_vertex_end > 0; +} + +void SymbolBucket::addGlyph(uint64_t tileid, const std::string stackname, + const std::u32string &string, const FontStack &fontStack, + GlyphAtlas &glyphAtlas, GlyphPositions &face) { + const std::map &sdfs = fontStack.getSDFs(); + // Loop through all characters and add glyph to atlas, positions. + for (uint32_t chr : string) { + auto sdf_it = sdfs.find(chr); + if (sdf_it != sdfs.end()) { + const SDFGlyph& sdf = sdf_it->second; + const Rect rect = glyphAtlas.addGlyph(tileid, stackname, sdf); + face.emplace(chr, Glyph{rect, sdf.metrics}); + } + } +} + +void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, + const Tile::ID &id, SpriteAtlas &spriteAtlas, GlyphAtlas &glyphAtlas, + GlyphStore &glyphStore) { + const bool text = properties.text.field.size(); + const bool icon = properties.icon.image.size(); + + util::utf8_to_utf32 ucs4conv; + + std::vector> labels; + + // Determine and load glyph ranges + { + std::set ranges; + + FilteredVectorTileLayer filtered_layer(layer, filter); + for (const pbf &feature_pbf : filtered_layer) { + VectorTileFeature feature {feature_pbf, layer}; + + if (text) { + std::string u8string = util::replaceTokens(properties.text.field, feature.properties); + + auto& convert = std::use_facet>(std::locale()); + if (properties.text.transform == TextTransformType::Uppercase) { + convert.toupper(&u8string[0], &u8string[0] + u8string.size()); + } else if (properties.text.transform == TextTransformType::Lowercase) { + convert.tolower(&u8string[0], &u8string[0] + u8string.size()); + } + + std::u32string string = ucs4conv.convert(u8string); + + if (string.size()) { + // Loop through all characters of this text and collect unique codepoints. + for (char32_t chr : string) { + ranges.insert(getGlyphRange(chr)); + } + + labels.emplace_back(string, feature.geometry); + } + } + + // TODO: refactor for simultaneous placement + // TODO: refactor to use quads instead of GL_POINTS + if (icon) { + std::string field = util::replaceTokens(properties.icon.image, feature.properties); + + // TODO: remove hardcoded size 12. + const Rect rect = spriteAtlas.getIcon(12, field); + const uint16_t tx = rect.x + rect.w / 2; + const uint16_t ty = rect.y + rect.h / 2; + + Geometry::command cmd; + pbf geom = feature.geometry; + Geometry geometry(geom); + int32_t x, y; + while ((cmd = geometry.next(x, y)) != Geometry::end) { + if (cmd == Geometry::move_to) { + iconVertexBuffer.add(x, y, tx, ty); + } else { + Log::Warning(Event::ParseTile, "other command than move_to in icon geometry"); + } + } + + icon_vertex_end = iconVertexBuffer.index(); + } + } + + glyphStore.waitForGlyphRanges(properties.text.font, ranges); + } + + + float horizontalAlign = 0.5; + if (properties.text.horizontal_align == TextHorizontalAlignType::Right) horizontalAlign = 1; + else if (properties.text.horizontal_align == TextHorizontalAlignType::Left) horizontalAlign = 0; + + float verticalAlign = 0.5; + if (properties.text.vertical_align == TextVerticalAlignType::Bottom) verticalAlign = 1; + else if (properties.text.vertical_align == TextVerticalAlignType::Top) verticalAlign = 0; + + + + // Create a copy! + const FontStack &fontStack = glyphStore.getFontStack(properties.text.font); + GlyphPositions face; + + + // Shape and place all labels. + for (const std::pair &label : labels) { + + // Shape labels. + const Shaping shaping = fontStack.getShaping(label.first, properties.text.max_width, + properties.text.line_height, horizontalAlign, + verticalAlign, properties.text.letter_spacing); + + addGlyph(id.to_uint64(), properties.text.font, label.first, fontStack, glyphAtlas, face); + + // Place labels. + addFeature(label.second, face, shaping); + } +} + +void SymbolBucket::addFeature(const pbf &geom_pbf, const GlyphPositions &face, + const Shaping &shaping) { + // Decode all lines. + std::vector line; + Geometry::command cmd; + + Coordinate coord; + pbf geom(geom_pbf); + Geometry geometry(geom); + int32_t x, y; + while ((cmd = geometry.next(x, y)) != Geometry::end) { + if (cmd == Geometry::move_to) { + if (!line.empty()) { + placement.addFeature(*this, line, properties, face, shaping); + line.clear(); + } + } + line.emplace_back(x, y); + } + if (line.size()) { + placement.addFeature(*this, line, properties, face, shaping); + } +} + +void SymbolBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, + PlacementRange placementRange, float zoom) { + placementZoom += zoom; + + for (const PlacedGlyph &glyph : glyphs) { + const auto &tl = glyph.tl; + const auto &tr = glyph.tr; + const auto &bl = glyph.bl; + const auto &br = glyph.br; + const auto &tex = glyph.tex; + const auto &angle = glyph.angle; + + float minZoom = util::max(static_cast(zoom + log(glyph.glyphBox.minScale) / log(2)), + placementZoom); + float maxZoom = + util::min(static_cast(zoom + log(glyph.glyphBox.maxScale) / log(2)), 25.0f); + const auto &glyphAnchor = glyph.glyphBox.anchor; + + if (maxZoom <= minZoom) + continue; + + // Lower min zoom so that while fading out the label + // it can be shown outside of collision-free zoom levels + if (minZoom == placementZoom) { + minZoom = 0; + } + + const int glyph_vertex_length = 4; + + if (!triangleGroups.size() || + (triangleGroups.back().vertex_length + glyph_vertex_length > 65535)) { + // Move to a new group because the old one can't hold the geometry. + triangleGroups.emplace_back(); + } + + // We're generating triangle fans, so we always start with the first + // coordinate in this polygon. + triangle_group_type &triangleGroup = triangleGroups.back(); + uint32_t triangleIndex = triangleGroup.vertex_length; + + // coordinates (2 triangles) + textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom, + placementRange, maxZoom, placementZoom); + textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle, + minZoom, placementRange, maxZoom, placementZoom); + textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle, + minZoom, placementRange, maxZoom, placementZoom); + textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, + angle, minZoom, placementRange, maxZoom, placementZoom); + + // add the two triangles, referencing the four coordinates we just inserted. + triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2); + triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3); + + triangleGroup.vertex_length += glyph_vertex_length; + triangleGroup.elements_length += 2; + } +} + +void SymbolBucket::drawGlyphs(TextShader &shader) { + char *vertex_index = BUFFER_OFFSET(text_vertex_start * textVertexBuffer.itemSize); + char *elements_index = + BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); + for (triangle_group_type &group : triangleGroups) { + group.array.bind(shader, textVertexBuffer, triangleElementsBuffer, vertex_index); + glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * textVertexBuffer.itemSize; + elements_index += group.elements_length * triangleElementsBuffer.itemSize; + } +} + +void SymbolBucket::drawIcons(IconShader& shader) { + char *vertex_index = BUFFER_OFFSET(icon_vertex_start * iconVertexBuffer.itemSize); + array.bind(shader, iconVertexBuffer, vertex_index); + glDrawArrays(GL_POINTS, 0, (GLsizei)(icon_vertex_end - icon_vertex_start)); +} + +void SymbolBucket::drawIcons(DotShader& shader) { + char *vertex_index = BUFFER_OFFSET(icon_vertex_start * iconVertexBuffer.itemSize); + array.bind(shader, iconVertexBuffer, vertex_index); + glDrawArrays(GL_POINTS, 0, (GLsizei)(icon_vertex_end - icon_vertex_start)); +} + +} diff --git a/src/renderer/text_bucket.cpp b/src/renderer/text_bucket.cpp deleted file mode 100644 index 45c7bf971f..0000000000 --- a/src/renderer/text_bucket.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -using namespace mbgl; - -TextBucket::TextBucket( - TextVertexBuffer &vertexBuffer, - TriangleElementsBuffer &triangleElementsBuffer, - const StyleBucketText &properties, Placement &placement) - : properties(properties), - vertexBuffer(vertexBuffer), - triangleElementsBuffer(triangleElementsBuffer), - placement(placement), - vertex_start(vertexBuffer.index()), - triangle_elements_start(triangleElementsBuffer.index()) {} - -void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, - PlacementRange placementRange, float zoom) { - placementZoom += zoom; - - for (const PlacedGlyph &glyph : glyphs) { - const auto &tl = glyph.tl; - const auto &tr = glyph.tr; - const auto &bl = glyph.bl; - const auto &br = glyph.br; - const auto &tex = glyph.tex; - const auto &angle = glyph.angle; - - float minZoom = util::max( - static_cast(zoom + log(glyph.glyphBox.minScale) / log(2)), - placementZoom); - float maxZoom = util::min( - static_cast(zoom + log(glyph.glyphBox.maxScale) / log(2)), - 25.0f); - const auto &glyphAnchor = glyph.glyphBox.anchor; - - if (maxZoom <= minZoom) - continue; - - // Lower min zoom so that while fading out the label - // it can be shown outside of collision-free zoom levels - if (minZoom == placementZoom) { - minZoom = 0; - } - - const int glyph_vertex_length = 4; - - if (!triangleGroups.size() || - (triangleGroups.back().vertex_length + glyph_vertex_length > - 65535)) { - // Move to a new group because the old one can't hold the geometry. - triangleGroups.emplace_back(); - } - - // We're generating triangle fans, so we always start with the first - // coordinate in this polygon. - triangle_group_type &triangleGroup = triangleGroups.back(); - uint32_t triangleIndex = triangleGroup.vertex_length; - - // coordinates (2 triangles) - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, - tex.y, angle, minZoom, placementRange, maxZoom, - placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, - tex.x + tex.w, tex.y, angle, minZoom, placementRange, - maxZoom, placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, - tex.y + tex.h, angle, minZoom, placementRange, - maxZoom, placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, - tex.x + tex.w, tex.y + tex.h, angle, minZoom, - placementRange, maxZoom, placementZoom); - - // add the two triangles, referencing the four coordinates we just - // inserted. - triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1, - triangleIndex + 2); - triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2, - triangleIndex + 3); - - triangleGroup.vertex_length += glyph_vertex_length; - triangleGroup.elements_length += 2; - } -}; - -void TextBucket::addFeature(const pbf &geom_pbf, - const GlyphPositions &face, - const Shaping &shaping) { - // Decode all lines. - std::vector line; - Geometry::command cmd; - - Coordinate coord; - pbf geom(geom_pbf); - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - if (!line.empty()) { - placement.addFeature(*this, line, properties, face, shaping); - line.clear(); - } - } - line.emplace_back(x, y); - } - if (line.size()) { - placement.addFeature(*this, line, properties, face, shaping); - } -} - -void TextBucket::render(Painter &painter, std::shared_ptr layer_desc, - const Tile::ID &id) { - painter.renderText(*this, layer_desc, id); -} - -bool TextBucket::hasData() const { - return !triangleGroups.empty(); -} - -void TextBucket::drawGlyphs(TextShader &shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - char *elements_index = - BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type &group : triangleGroups) { - group.array.bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; - } -} diff --git a/src/style/property_fallback.cpp b/src/style/property_fallback.cpp index fb291034e9..4401c8105a 100644 --- a/src/style/property_fallback.cpp +++ b/src/style/property_fallback.cpp @@ -33,6 +33,7 @@ const std::map PropertyFallbackValue::properties = { { PropertyKey::IconTranslateX, defaultStyleProperties().icon.translate[0] }, { PropertyKey::IconTranslateY, defaultStyleProperties().icon.translate[1] }, { PropertyKey::IconTranslateAnchor, defaultStyleProperties().icon.translate_anchor }, + { PropertyKey::TextOpacity, defaultStyleProperties().text.opacity }, { PropertyKey::TextSize, defaultStyleProperties().text.size }, { PropertyKey::TextColor, defaultStyleProperties().text.color }, diff --git a/src/style/style_bucket.cpp b/src/style/style_bucket.cpp index afd4bc09f7..9a40c2386b 100644 --- a/src/style/style_bucket.cpp +++ b/src/style/style_bucket.cpp @@ -6,8 +6,7 @@ StyleBucket::StyleBucket(StyleLayerType type) { switch (type) { case StyleLayerType::Fill: render = StyleBucketFill{}; break; case StyleLayerType::Line: render = StyleBucketLine{}; break; - case StyleLayerType::Icon: render = StyleBucketIcon{}; break; - case StyleLayerType::Text: render = StyleBucketText{}; break; + case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break; case StyleLayerType::Raster: render = StyleBucketRaster{}; break; default: break; } diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp index df94ef9524..3d5ffd6a43 100644 --- a/src/style/style_layer.cpp +++ b/src/style/style_layer.cpp @@ -191,23 +191,30 @@ void StyleLayer::applyStyleProperties(const float z, const times } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { - properties.set(); - IconProperties &icon = properties.get(); - applyStyleProperty(PropertyKey::IconOpacity, icon.opacity, z, now); - applyStyleProperty(PropertyKey::IconRotate, icon.rotate, z, now); -} +void StyleLayer::applyStyleProperties(const float z, const timestamp now) { + properties.set(); + SymbolProperties &symbol = properties.get(); + applyStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now); + applyStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now); + applyStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now); + applyStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now); + applyStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now); + applyStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now); + applyStyleProperty(PropertyKey::IconHaloBlur, symbol.icon.halo_blur, z, now); + applyStyleProperty(PropertyKey::IconTranslateX, symbol.icon.translate[0], z, now); + applyStyleProperty(PropertyKey::IconTranslateY, symbol.icon.translate[1], z, now); + applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now); + + applyStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now); + applyStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now); + applyStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now); + applyStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now); + applyStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now); + applyStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now); + applyStyleProperty(PropertyKey::TextTranslateX, symbol.text.translate[0], z, now); + applyStyleProperty(PropertyKey::TextTranslateY, symbol.text.translate[1], z, now); + applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now); -template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { - properties.set(); - TextProperties &text = properties.get(); - applyStyleProperty(PropertyKey::TextOpacity, text.opacity, z, now); - applyStyleProperty(PropertyKey::TextSize, text.size, z, now); - applyStyleProperty(PropertyKey::TextColor, text.color, z, now); - applyStyleProperty(PropertyKey::TextHaloColor, text.halo_color, z, now); - applyStyleProperty(PropertyKey::TextHaloWidth, text.halo_width, z, now); - applyStyleProperty(PropertyKey::TextHaloBlur, text.halo_blur, z, now); } template <> @@ -247,8 +254,7 @@ void StyleLayer::updateProperties(float z, const timestamp now) { switch (type) { case StyleLayerType::Fill: applyStyleProperties(z, now); break; case StyleLayerType::Line: applyStyleProperties(z, now); break; - case StyleLayerType::Icon: applyStyleProperties(z, now); break; - case StyleLayerType::Text: applyStyleProperties(z, now); break; + case StyleLayerType::Symbol: applyStyleProperties(z, now); break; case StyleLayerType::Raster: applyStyleProperties(z, now); break; case StyleLayerType::Composite: applyStyleProperties(z, now); break; case StyleLayerType::Background: applyStyleProperties(z, now); break; diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index 0bed3381cd..3377185d32 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -63,6 +63,20 @@ JSVal StyleParser::replaceConstant(JSVal value) { #pragma mark - Parse Render Properties +template<> bool StyleParser::parseRenderProperty(JSVal value, bool &target, const char *name) { + if (value.HasMember(name)) { + JSVal property = replaceConstant(value[name]); + if (property.IsBool()) { + target = property.GetBool(); + return true; + } else { + fprintf(stderr, "[WARNING] '%s' must be a boolean\n", name); + } + } + return false; +} + + template<> bool StyleParser::parseRenderProperty(JSVal value, std::string &target, const char *name) { if (value.HasMember(name)) { JSVal property = replaceConstant(value[name]); @@ -139,12 +153,12 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, vec2 &targe return false; } -template -bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser) { +template +bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name) { if (value.HasMember(name)) { JSVal property = replaceConstant(value[name]); if (property.IsString()) { - target = parser({ property.GetString(), property.GetStringLength() }); + target = Parser({ property.GetString(), property.GetStringLength() }); return true; } else { fprintf(stderr, "[WARNING] %s must have one of the enum values\n", name); @@ -167,7 +181,7 @@ void StyleParser::parseSources(JSVal value) { int32_t min_zoom = 0; int32_t max_zoom = 22; - parseRenderProperty(itr->value, type, "type", parseSourceType); + parseRenderProperty(itr->value, type, "type"); parseRenderProperty(itr->value, url, "url"); if (type == SourceType::Raster) { parseRenderProperty(itr->value, tile_size, "tileSize"); @@ -348,19 +362,19 @@ template<> std::tuple StyleParser::parseProperty(JSVal value, template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name); - return std::tuple { false, TranslateAnchorType::Default }; + return std::tuple { false, TranslateAnchorType::Map }; } - return std::tuple { true, parseTranslateAnchorType({ value.GetString(), value.GetStringLength() }) }; + return std::tuple { true, TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name); - return std::tuple { false, RotateAnchorType::Default }; + return std::tuple { false, RotateAnchorType::Map }; } - return std::tuple { true, parseRotateAnchorType({ value.GetString(), value.GetStringLength() }) }; + return std::tuple { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { @@ -592,6 +606,18 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) { parseOptionalProperty>("icon-opacity", Key::IconOpacity, klass, value); parseOptionalProperty("transition-icon-opacity", Key::IconOpacity, klass, value); parseOptionalProperty>("icon-rotate", Key::IconRotate, klass, value); + parseOptionalProperty>("icon-size", Key::IconSize, klass, value); + parseOptionalProperty("transition-icon-size", Key::IconSize, klass, value); + parseOptionalProperty>("icon-color", Key::IconColor, klass, value); + parseOptionalProperty("transition-icon-color", Key::IconColor, klass, value); + parseOptionalProperty>("icon-halo-color", Key::IconHaloColor, klass, value); + parseOptionalProperty("transition-icon-halo-color", Key::IconHaloColor, klass, value); + parseOptionalProperty>("icon-halo-width", Key::IconHaloWidth, klass, value); + parseOptionalProperty("transition-icon-halo-width", Key::IconHaloWidth, klass, value); + parseOptionalProperty>("icon-halo-blur", Key::IconHaloBlur, klass, value); + parseOptionalProperty("transition-icon-halo-blur", Key::IconHaloBlur, klass, value); + parseOptionalProperty>("icon-translate", { Key::IconTranslateX, Key::IconTranslateY }, klass, value); + parseOptionalProperty("transition-icon-translate", Key::IconTranslate, klass, value); parseOptionalProperty>("text-opacity", Key::TextOpacity, klass, value); parseOptionalProperty("transition-text-opacity", Key::TextOpacity, klass, value); @@ -605,6 +631,8 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) { parseOptionalProperty("transition-text-halo-width", Key::TextHaloWidth, klass, value); parseOptionalProperty>("text-halo-blur", Key::TextHaloBlur, klass, value); parseOptionalProperty("transition-text-halo-blur", Key::TextHaloBlur, klass, value); + parseOptionalProperty>("text-translate", { Key::TextTranslateX, Key::TextTranslateY }, klass, value); + parseOptionalProperty("transition-text-translate", Key::TextTranslate, klass, value); parseOptionalProperty>("composite-opacity", Key::CompositeOpacity, klass, value); parseOptionalProperty("transition-composite-opacity", Key::CompositeOpacity, klass, value); @@ -833,61 +861,63 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { case StyleLayerType::Fill: { StyleBucketFill &render = bucket.render.get(); - parseRenderProperty(value, render.winding, "fill-winding", parseWindingType); + parseRenderProperty(value, render.winding, "fill-winding"); } break; case StyleLayerType::Line: { StyleBucketLine &render = bucket.render.get(); - parseRenderProperty(value, render.cap, "line-cap", parseCapType); - parseRenderProperty(value, render.join, "line-join", parseJoinType); + parseRenderProperty(value, render.cap, "line-cap"); + parseRenderProperty(value, render.join, "line-join"); parseRenderProperty(value, render.miter_limit, "line-miter-limit"); parseRenderProperty(value, render.round_limit, "line-round-limit"); } break; - case StyleLayerType::Icon: { - StyleBucketIcon &render = bucket.render.get(); - - parseRenderProperty(value, render.size, "icon-size"); - parseRenderProperty(value, render.icon, "icon-image"); - parseRenderProperty(value, render.spacing, "icon-spacing"); - parseRenderProperty(value, render.padding, "icon-padding"); - if (parseRenderProperty(value, render.translate, "icon-translate")) { - render.translate.x *= 24; - render.translate.y *= -24; - } - parseRenderProperty(value, render.translate_anchor, "icon-translate-anchor", parseTranslateAnchorType); - } break; - - case StyleLayerType::Text: { - StyleBucketText &render = bucket.render.get(); - - parseRenderProperty(value, render.field, "text-field"); - parseRenderProperty(value, render.path, "text-path", parseTextPathType); - parseRenderProperty(value, render.transform, "text-transform", parseTextTransformType); - parseRenderProperty(value, render.font, "text-font"); - parseRenderProperty(value, render.max_size, "text-max-size"); - if (parseRenderProperty(value, render.max_width, "text-max-width")) { - render.max_width *= 24; // em - } - if (parseRenderProperty(value, render.line_height, "text-line-height")) { - render.line_height *= 24; // em - } - if (parseRenderProperty(value, render.letter_spacing, "text-letter-spacing")) { - render.letter_spacing *= 24; // em - } - parseRenderProperty(value, render.alignment, "text-alignment", parseAlignmentType); - parseRenderProperty(value, render.vertical_alignment, "text-vertical-alignment", parseVerticalAlignmentType); - parseRenderProperty(value, render.max_angle_delta, "text-max-angle"); - parseRenderProperty(value, render.min_distance, "text-min-distance"); - parseRenderProperty(value, render.rotate, "text-rotate"); - parseRenderProperty(value, render.slant, "text-slant"); - parseRenderProperty(value, render.padding, "text-padding"); - if (parseRenderProperty(value, render.translate, "text-translate")) { - render.translate.x *= 24; - render.translate.y *= -24; - } - parseRenderProperty(value, render.translate_anchor, "text-translate-anchor", parseTranslateAnchorType); + case StyleLayerType::Symbol: { + StyleBucketSymbol &render = bucket.render.get(); + + parseRenderProperty(value, render.placement, "symbol-placement"); + parseRenderProperty(value, render.min_distance, "symbol-min-distance"); + + parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap"); + parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement"); + parseRenderProperty(value, render.icon.optional, "icon-optional"); + parseRenderProperty(value, render.icon.rotation_alignment, "icon-rotation-alignment"); + parseRenderProperty(value, render.icon.max_size, "icon-max-size"); + parseRenderProperty(value, render.icon.image, "icon-image"); + parseRenderProperty(value, render.icon.padding, "icon-padding"); + parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright"); + parseRenderProperty(value, render.icon.offset, "icon-offset"); + parseRenderProperty(value, render.icon.translate_anchor, "icon-translate-anchor"); + + + parseRenderProperty(value, render.text.rotation_alignment, "text-rotation-alignment"); + parseRenderProperty(value, render.text.field, "text-field"); + parseRenderProperty(value, render.text.font, "text-font"); + parseRenderProperty(value, render.text.max_size, "text-max-size"); + if (parseRenderProperty(value, render.text.max_width, "text-max-width")) { + render.text.max_width *= 24; // em + } + if (parseRenderProperty(value, render.text.line_height, "text-line-height")) { + render.text.line_height *= 24; // em + } + if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) { + render.text.letter_spacing *= 24; // em + } + parseRenderProperty(value, render.text.justify, "text-justify"); + parseRenderProperty(value, render.text.horizontal_align, "text-horizontal-align"); + parseRenderProperty(value, render.text.vertical_align, "text-vertical-align"); + parseRenderProperty(value, render.text.max_angle_delta, "text-max-angle"); + parseRenderProperty(value, render.text.rotate, "text-rotate"); + parseRenderProperty(value, render.text.slant, "text-slant"); + parseRenderProperty(value, render.text.padding, "text-padding"); + parseRenderProperty(value, render.text.keep_upright, "text-keep-upright"); + parseRenderProperty(value, render.text.transform, "text-transform"); + parseRenderProperty(value, render.text.offset, "text-offset"); + parseRenderProperty(value, render.text.translate_anchor, "text-translate-anchor"); + parseRenderProperty(value, render.text.allow_overlap, "text-allow-overlap"); + parseRenderProperty(value, render.text.ignore_placement, "text-ignore-placement"); + parseRenderProperty(value, render.text.optional, "text-optional"); } break; default: // There are no render properties for these layer types. diff --git a/src/style/style_properties.cpp b/src/style/style_properties.cpp index 3908d35ffa..1b05528d2f 100644 --- a/src/style/style_properties.cpp +++ b/src/style/style_properties.cpp @@ -4,8 +4,6 @@ namespace mbgl { template<> const FillProperties &defaultStyleProperties() { static const FillProperties p; return p; } template<> const LineProperties &defaultStyleProperties() { static const LineProperties p; return p; } -template<> const IconProperties &defaultStyleProperties() { static const IconProperties p; return p; } -template<> const TextProperties &defaultStyleProperties() { static const TextProperties p; return p; } template<> const SymbolProperties &defaultStyleProperties() { static const SymbolProperties p; return p; } template<> const CompositeProperties &defaultStyleProperties() { static const CompositeProperties p; return p; } template<> const RasterProperties &defaultStyleProperties() { static const RasterProperties p; return p; } diff --git a/src/text/placement.cpp b/src/text/placement.cpp index b15266137b..82c45b4e0c 100644 --- a/src/text/placement.cpp +++ b/src/text/placement.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include @@ -245,11 +245,11 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes, } } -void Placement::addFeature(TextBucket &bucket, const std::vector &line, - const StyleBucketText &info, const GlyphPositions &face, +void Placement::addFeature(SymbolBucket &bucket, const std::vector &line, + const StyleBucketSymbol &info, const GlyphPositions &face, const Shaping &shaping) { - const bool horizontal = info.path == TextPathType::Horizontal; - const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.max_size); + const bool horizontal = info.text.rotation_alignment == RotationAlignmentType::Viewport; + const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.text.max_size); Anchors anchors; @@ -272,10 +272,10 @@ void Placement::addFeature(TextBucket &bucket, const std::vector &li PlacedGlyphs glyphs; GlyphBoxes boxes; - getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, line, - info.max_angle_delta * (M_PI/180), info.rotate); + getGlyphs(glyphs, boxes, anchor, info.text.offset, shaping, face, fontScale, horizontal, line, + info.text.max_angle_delta * (M_PI/180), info.text.rotate); PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale, - info.padding, horizontal, info.always_visible); + info.text.padding, horizontal, info.text.ignore_placement); if (place) { bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset); } -- cgit v1.2.1