summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-04-15 12:46:19 -0400
committerKonstantin Käfer <mail@kkaefer.com>2014-04-15 12:46:19 -0400
commit78c10c0701a85dd4f74d8fb249847f8178b1b6e1 (patch)
tree3af6101bfe3e19550234241ce4e3ef068decf515
parent8e442411b0339072666f2b434963a0bd88215f3d (diff)
parent228ce3327855bd8b41d5f971902de8a8f3269115 (diff)
downloadqtlocation-mapboxgl-78c10c0701a85dd4f74d8fb249847f8178b1b6e1.tar.gz
Merge remote-tracking branch 'remotes/origin/pr/127'
Conflicts: include/llmr/map/map.hpp src/map/map.cpp src/platform/platform.cpp
-rw-r--r--bin/style.js158
-rw-r--r--include/llmr/map/coverage.hpp14
-rw-r--r--include/llmr/map/map.hpp23
-rw-r--r--include/llmr/map/source.hpp67
-rw-r--r--include/llmr/map/tile_data.hpp8
-rw-r--r--include/llmr/map/tile_parser.hpp6
-rw-r--r--include/llmr/map/transform.hpp3
-rw-r--r--include/llmr/platform/platform.hpp4
-rw-r--r--include/llmr/renderer/painter.hpp5
-rw-r--r--include/llmr/renderer/raster_bucket.hpp20
-rw-r--r--include/llmr/shader/plain_shader.hpp3
-rw-r--r--include/llmr/style/bucket_description.hpp4
-rw-r--r--include/llmr/style/class_description.hpp1
-rw-r--r--include/llmr/style/properties.hpp12
-rw-r--r--include/llmr/style/style.hpp1
-rw-r--r--include/llmr/style/style_parser.hpp1
-rw-r--r--src/map/coverage.cpp113
-rw-r--r--src/map/map.cpp300
-rw-r--r--src/map/source.cpp379
-rw-r--r--src/map/tile_data.cpp27
-rw-r--r--src/map/tile_parser.cpp56
-rw-r--r--src/map/transform.cpp2
-rw-r--r--src/platform/platform.cpp5
-rw-r--r--src/renderer/painter.cpp61
-rw-r--r--src/renderer/raster_bucket.cpp14
-rw-r--r--src/shader/plain.fragment.glsl3
-rw-r--r--src/shader/plain_shader.cpp9
-rw-r--r--src/shader/shaders_gl.cpp2
-rw-r--r--src/shader/shaders_gles2.cpp2
-rw-r--r--src/style/style.cpp16
-rw-r--r--src/style/style_parser.cpp22
31 files changed, 874 insertions, 467 deletions
diff --git a/bin/style.js b/bin/style.js
index c262dac0ef..b9d404a9e9 100644
--- a/bin/style.js
+++ b/bin/style.js
@@ -2,93 +2,97 @@
module.exports = {
"buckets": {
+ "satellite": {
+ "source": "satellite",
+ "type": "raster"
+ },
"water": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "water",
"type": "fill"
},
"road_large": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "road", "field": "class", "value": ["motorway", "main"],
"type": "line", "cap": "round", "join": "round"
},
"road_regular": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "road", "field": "class", "value": "street",
"type": "line", "cap": "round", "join": "round"
},
"road_limited": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "road", "field": "class", "value": "street_limited",
"type": "line", "cap": "round", "join": "round"
},
"park": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "landuse", "field": "class", "value": "park",
"type": "fill"
},
"wood": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "landuse", "field": "class", "value": "wood",
"type": "fill"
},
"school": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "landuse", "field": "class", "value": "school",
"type": "fill"
},
"cemetery": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "landuse", "field": "class", "value": "cemetery",
"type": "fill"
},
"industrial": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "landuse", "field": "class", "value": "industrial",
"type": "fill"
},
"building": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "building",
"type": "fill"
},
"alcohol_poi": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "poi_label",
"field": "type",
"value": ["Alcohol"],
"type": "point"
},
"cafe_poi": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "poi_label",
"field": "type",
"value": ["Cafe"],
"type": "point"
},
"embassy_poi": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "poi_label",
"field": "type",
"value": ["Embassy"],
"type": "point"
},
"park_poi": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "poi_label",
"field": "type",
"value": ["Park"],
"type": "point"
},
"restaurant_poi": {
- "source": "streets",
+ "source": "mapbox streets",
"layer": "poi_label",
"field": "type",
"value": ["Restaurant"],
"type": "point"
},
"country_label": {
- "source": "streets",
+ "source": "mapbox streets",
"type": "text",
"layer": "country_label",
"feature_type": "point",
@@ -99,7 +103,7 @@ module.exports = {
"fontSize": 16
},
"place_label": {
- "source": "streets",
+ "source": "mapbox streets",
"type": "text",
"layer": "place_label",
"feature_type": "point",
@@ -110,7 +114,7 @@ module.exports = {
"alwaysVisible": true
},
"road_label": {
- "source": "streets",
+ "source": "mapbox streets",
"type": "text",
"layer": "road_label",
"feature_type": "line",
@@ -128,6 +132,7 @@ module.exports = {
"text": "#000000",
},
"structure": [
+ { "name": "satellite", "bucket": "satellite" },
{ "name": "park", "bucket": "park" },
{ "name": "wood", "bucket": "wood" },
{ "name": "water", "bucket": "water" },
@@ -265,6 +270,123 @@ module.exports = {
"size": ["exponential", 14, 8, 1, 8, 12]
},
}
+ },
+ {
+ "name": "satellite",
+ "layers": {
+ "background": {
+ "type": "background",
+ "color": "#333333",
+ "opacity": 1.0
+ },
+ "park": {
+ "type": "fill",
+ "opacity": 0,
+ },
+ "wood": {
+ "type": "fill",
+ "opacity": 0,
+ },
+ "water": {
+ "type": "fill",
+ "opacity": 0,
+ },
+ "building": {
+ "type": "fill",
+ "opacity": 0,
+ },
+ "road_limited": {
+ "type": "line",
+ "color": "#BBBBBB",
+ "width": [
+ "stops",
+ { z: 0, val: 1 },
+ { z: 30, val: 1 }
+ ]
+ },
+ "road_regular": {
+ "type": "line",
+ "color": "#999999",
+ "width": [
+ "stops",
+ { z: 0, val: 0.5 },
+ { z: 13, val: 0.5 },
+ { z: 16, val: 2 },
+ { z: 20, val: 16 },
+ { z: 30, val: 16 }
+ ],
+ },
+ "road_large": {
+ "type": "line",
+ "color": "#666666",
+ "width": [
+ "stops",
+ { z: 0, val: 0.5 },
+ { z: 11, val: 0.5 },
+ { z: 13, val: 1 },
+ { z: 16, val: 4 },
+ { z: 20, val: 32 },
+ { z: 30, val: 32 }
+ ],
+ },
+ "alcohol_poi": {
+ "type": "point",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "alcohol-shop",
+ "enabled": [ "min", 17 ]
+ },
+ "cafe_poi": {
+ "type": "point",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "cafe",
+ "enabled": [ "min", 17 ]
+ },
+ "embassy_poi": {
+ "type": "point",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "embassy",
+ "enabled": [ "min", 17 ]
+ },
+ "park_poi": {
+ "type": "point",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "park",
+ "enabled": [ "min", 17 ]
+ },
+ "restaurant_poi": {
+ "type": "point",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "restaurant",
+ "enabled": [ "min", 17 ]
+ },
+ "country_label": {
+ "type": "text",
+ "stroke": [ 1, 1, 1, 0.7 ],
+ "color": "text",
+ "size": 16
+ },
+ "place_label": {
+ "type": "text",
+ "stroke": [1,1,1,0.7],
+ "color": "text",
+ "size": 18
+ },
+ "road_label": {
+ "type": "text",
+ "color": "text",
+ "stroke": [1,1,1,0.7],
+ "size": ["exponential", 14, 8, 1, 8, 12]
+ },
+ "satellite": {
+ "type": "raster",
+ "opacity": 0.25
+ },
+ }
}
]
};
diff --git a/include/llmr/map/coverage.hpp b/include/llmr/map/coverage.hpp
deleted file mode 100644
index ed6317a560..0000000000
--- a/include/llmr/map/coverage.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef LLMR_MAP_COVERAGE
-#define LLMR_MAP_COVERAGE
-
-#include <llmr/map/tile.hpp>
-
-#include <forward_list>
-
-namespace llmr {
-
-std::forward_list<Tile::ID> covering_tiles(int32_t zoom, const box& points, const bool use_raster = false, const bool use_retina = false);
-
-}
-
-#endif
diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp
index 640786e105..fef3673860 100644
--- a/include/llmr/map/map.hpp
+++ b/include/llmr/map/map.hpp
@@ -5,7 +5,6 @@
#include <llmr/map/tile_data.hpp>
#include <llmr/map/transform.hpp>
#include <llmr/style/style.hpp>
-#include <llmr/style/style.hpp>
#include <llmr/geometry/glyph_atlas.hpp>
#include <llmr/renderer/painter.hpp>
#include <llmr/util/noncopyable.hpp>
@@ -13,10 +12,12 @@
#include <cstdint>
#include <string>
+#include <map>
namespace llmr {
class Settings;
+class Source;
class Map : private util::noncopyable {
public:
@@ -28,7 +29,6 @@ public:
void loadStyle(const uint8_t *const data, uint32_t bytes);
void loadSettings();
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
- void toggleRaster();
/* callback */
void update();
@@ -64,14 +64,15 @@ public:
void stopRotating();
void toggleDebug();
+ void toggleRaster();
+
+ box cornersToBox(uint32_t z) const;
+ float getPixelRatio() const;
+ Style& getStyle();
+ GlyphAtlas& getGlyphAtlas();
private:
- bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
- bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
bool updateTiles();
- TileData::State addTile(const Tile::ID& id);
- TileData::State hasTile(const Tile::ID& id);
-
private:
Settings& settings;
@@ -81,16 +82,12 @@ private:
GlyphAtlas glyphAtlas;
Painter painter;
- bool use_raster = false;
+ std::map<std::string, Source> sources;
int32_t min_zoom;
int32_t max_zoom;
- float pixel_ratio;
-
-
- std::forward_list<Tile> tiles;
- std::forward_list<std::weak_ptr<TileData>> tile_data;
+ // float pixel_ratio;
};
}
diff --git a/include/llmr/map/source.hpp b/include/llmr/map/source.hpp
new file mode 100644
index 0000000000..3ddebb0ee4
--- /dev/null
+++ b/include/llmr/map/source.hpp
@@ -0,0 +1,67 @@
+#ifndef LLMR_MAP_SOURCE
+#define LLMR_MAP_SOURCE
+
+#include <llmr/map/tile.hpp>
+#include <llmr/map/tile_data.hpp>
+
+#include <forward_list>
+#include <memory>
+#include <list>
+#include <string>
+
+namespace llmr {
+
+class Map;
+class Transform;
+class Painter;
+class Texturepool;
+
+class Source : public std::enable_shared_from_this<Source> {
+public:
+ enum class Type {
+ vector,
+ raster
+ };
+
+public:
+ Source(Map& map, Transform& transform, Painter& painter, Texturepool& texturepool, const char *url = "", Type type = Type::vector, std::list<uint32_t> zooms = {0}, uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 14, bool enabled = true);
+
+ bool update();
+ void prepare_render(bool is_baselayer = false);
+ void render(double animationTime, bool is_baselayer = false);
+
+public:
+ bool enabled;
+
+private:
+ bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
+ bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
+ std::forward_list<Tile::ID> covering_tiles(int32_t zoom, const box& points);
+
+ bool updateTiles();
+
+ TileData::State addTile(const Tile::ID& id);
+ TileData::State hasTile(const Tile::ID& id);
+
+ double getZoom() const;
+
+private:
+ Map& map;
+ Transform& transform;
+ Painter& painter;
+ Texturepool& texturepool;
+
+ Type type;
+ std::list<uint32_t> zooms;
+ const char *url;
+ uint32_t tile_size;
+ uint32_t min_zoom;
+ uint32_t max_zoom;
+
+ std::forward_list<Tile> tiles;
+ std::forward_list<std::weak_ptr<TileData>> tile_data;
+};
+
+}
+
+#endif
diff --git a/include/llmr/map/tile_data.hpp b/include/llmr/map/tile_data.hpp
index 955c1ffbc8..c6283e3706 100644
--- a/include/llmr/map/tile_data.hpp
+++ b/include/llmr/map/tile_data.hpp
@@ -35,7 +35,6 @@ class Raster;
class LayerDescription;
class BucketDescription;
-
class PlainShader;
class TileData : public std::enable_shared_from_this<TileData>,
@@ -57,7 +56,7 @@ public:
};
public:
- TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas, const bool use_raster = false, const bool use_retina = false);
+ TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas, const std::string url, const bool is_raster);
~TileData();
void request();
@@ -68,8 +67,6 @@ public:
public:
const Tile::ID id;
- const bool use_raster;
- const bool use_retina;
std::atomic<State> state;
std::shared_ptr<Raster> raster;
@@ -91,7 +88,8 @@ public:
std::map<std::string, std::unique_ptr<Bucket>> buckets;
private:
- // Source data
+ const std::string url;
+ const bool is_raster = false;
std::string data;
const Style& style;
GlyphAtlas& glyphAtlas;
diff --git a/include/llmr/map/tile_parser.hpp b/include/llmr/map/tile_parser.hpp
index b99f9aaf94..ee623f1ff9 100644
--- a/include/llmr/map/tile_parser.hpp
+++ b/include/llmr/map/tile_parser.hpp
@@ -8,7 +8,7 @@ namespace llmr {
class TileParser {
public:
- TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas);
+ TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas, bool is_raster = false);
private:
bool obsolete() const;
@@ -19,10 +19,12 @@ private:
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
+ std::unique_ptr<Bucket> createRasterBucket(const BucketDescription& bucket_desc);
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc);
private:
- const VectorTile data;
+ const VectorTile vector_data;
+ const std::string raster_data;
TileData& tile;
const Style& style;
GlyphAtlas& glyphAtlas;
diff --git a/include/llmr/map/transform.hpp b/include/llmr/map/transform.hpp
index 7b4d59ee17..e81b9e4121 100644
--- a/include/llmr/map/transform.hpp
+++ b/include/llmr/map/transform.hpp
@@ -56,8 +56,7 @@ public:
void startScaling();
void stopScaling();
- // Temporary
- box mapCornersToBox(uint32_t z) const;
+ box cornersToBox(uint32_t z) const;
// More getters
inline uint16_t getWidth() const { return width; }
diff --git a/include/llmr/platform/platform.hpp b/include/llmr/platform/platform.hpp
index 58292fc07f..d66a8f7746 100644
--- a/include/llmr/platform/platform.hpp
+++ b/include/llmr/platform/platform.hpp
@@ -7,11 +7,7 @@
namespace llmr {
-extern const char *kTileVectorURL;
-extern const char *kTileRasterURL;
extern const char *kSpriteURL;
-extern const int32_t kTileVectorMaxZoom;
-extern const int32_t kTileRasterMaxZoom;
namespace platform {
diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp
index 3a96f81001..5cca820ecc 100644
--- a/include/llmr/renderer/painter.hpp
+++ b/include/llmr/renderer/painter.hpp
@@ -28,6 +28,7 @@ class FillBucket;
class LineBucket;
class PointBucket;
class TextBucket;
+class RasterBucket;
class Painter : private util::noncopyable {
public:
@@ -42,17 +43,17 @@ public:
void renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& id);
+ void renderRaster(const std::string& layer_name, const std::shared_ptr<TileData>& tile_data);
void resize();
void prepareClippingMask();
- void drawClippingMask(const mat4& matrix, uint8_t clip_id, bool opaque = true);
+ void drawClippingMask(const mat4& matrix, uint8_t clip_id);
void finishClippingMask();
bool needsAnimation() const;
private:
void setupShaders();
- void renderRaster(const std::shared_ptr<TileData>& tile);
void renderLayers(const std::shared_ptr<TileData>& tile, const std::vector<LayerDescription>& layers);
void renderLayer(const std::shared_ptr<TileData>& tile_data, const LayerDescription& layer_desc);
void renderDebug(const std::shared_ptr<TileData>& tile);
diff --git a/include/llmr/renderer/raster_bucket.hpp b/include/llmr/renderer/raster_bucket.hpp
new file mode 100644
index 0000000000..88bb3bd846
--- /dev/null
+++ b/include/llmr/renderer/raster_bucket.hpp
@@ -0,0 +1,20 @@
+#ifndef LLMR_RENDERER_RASTERBUCKET
+#define LLMR_RENDERER_RASTERBUCKET
+
+#include <llmr/renderer/bucket.hpp>
+#include <llmr/style/bucket_description.hpp>
+
+namespace llmr {
+
+class BucketDescription;
+
+class RasterBucket : public Bucket {
+public:
+ RasterBucket(const BucketDescription& bucket_desc);
+
+ virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
+};
+
+}
+
+#endif
diff --git a/include/llmr/shader/plain_shader.hpp b/include/llmr/shader/plain_shader.hpp
index bf768f8845..d833375eb5 100644
--- a/include/llmr/shader/plain_shader.hpp
+++ b/include/llmr/shader/plain_shader.hpp
@@ -13,12 +13,15 @@ public:
void setColor(float r, float g, float b, float a);
void setColor(const std::array<float, 4>& color);
+ void setOpacity(float opacity);
private:
int32_t a_pos = -1;
std::array<float, 4> color = {{}};
int32_t u_color = -1;
+ float opacity = 0.0f;
+ float u_opacity = 0.0f;
};
}
diff --git a/include/llmr/style/bucket_description.hpp b/include/llmr/style/bucket_description.hpp
index 498e935f2d..e7cde3db8c 100644
--- a/include/llmr/style/bucket_description.hpp
+++ b/include/llmr/style/bucket_description.hpp
@@ -14,7 +14,8 @@ enum class BucketType {
Fill = 1,
Line = 2,
Point = 3,
- Text = 4
+ Text = 4,
+ Raster = 5
};
enum class CapType {
@@ -42,6 +43,7 @@ inline BucketType bucketType(const std::string& type) {
else if (type == "line") return BucketType::Line;
else if (type == "point") return BucketType::Point;
else if (type == "text") return BucketType::Text;
+ else if (type == "raster") return BucketType::Raster;
else return BucketType::None;
}
diff --git a/include/llmr/style/class_description.hpp b/include/llmr/style/class_description.hpp
index e98aee0c56..dc8c5faa25 100644
--- a/include/llmr/style/class_description.hpp
+++ b/include/llmr/style/class_description.hpp
@@ -15,6 +15,7 @@ public:
std::map<std::string, LineClass> line;
std::map<std::string, PointClass> point;
std::map<std::string, TextClass> text;
+ std::map<std::string, RasterClass> raster;
};
diff --git a/include/llmr/style/properties.hpp b/include/llmr/style/properties.hpp
index 47b6ff195c..8baa6cfef4 100644
--- a/include/llmr/style/properties.hpp
+++ b/include/llmr/style/properties.hpp
@@ -128,13 +128,23 @@ struct TextProperties {
struct BackgroundClass {
Color color = {{ 1, 1, 1, 1 }};
+ FunctionProperty opacity = 1;
};
-
struct BackgroundProperties {
Color color = {{ 1, 1, 1, 1 }};
+ float opacity = 1.0;
+};
+
+struct RasterClass {
+ FunctionProperty enabled = true;
+ FunctionProperty opacity = 1;
};
+struct RasterProperties {
+ bool enabled = true;
+ float opacity = 1.0;
+};
}
diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp
index a004df438a..2f1e42c4fd 100644
--- a/include/llmr/style/style.hpp
+++ b/include/llmr/style/style.hpp
@@ -47,6 +47,7 @@ public:
std::map<std::string, LineProperties> lines;
std::map<std::string, PointProperties> points;
std::map<std::string, TextProperties> texts;
+ std::map<std::string, RasterProperties> rasters;
} computed;
};
diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp
index 60bb9a0504..a096a81a8b 100644
--- a/include/llmr/style/style_parser.hpp
+++ b/include/llmr/style/style_parser.hpp
@@ -25,6 +25,7 @@ private:
PointClass parsePointClass(JSVal value);
TextClass parseTextClass(JSVal value);
BackgroundClass parseBackgroundClass(JSVal value);
+ RasterClass parseRasterClass(JSVal value);
bool parseBoolean(JSVal value);
std::string parseString(JSVal value);
diff --git a/src/map/coverage.cpp b/src/map/coverage.cpp
deleted file mode 100644
index b98518a588..0000000000
--- a/src/map/coverage.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <llmr/map/coverage.hpp>
-#include <llmr/util/constants.hpp>
-#include <llmr/util/vec.hpp>
-
-#include <functional>
-#include <cmath>
-
-// Taken from polymaps src/Layer.js
-// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
-
-struct edge {
- double x0 = 0, y0 = 0;
- double x1 = 0, y1 = 0;
- double dx = 0, dy = 0;
- edge(double x0, double y0, double x1, double y1, double dx, double dy)
- : x0(x0), y0(y0), x1(x1), y1(y1), dx(dx), dy(dy) {}
-};
-
-typedef const std::function<void(int32_t, int32_t, int32_t, int32_t)> ScanLine;
-
-// scan-line conversion
-edge _edge(const llmr::vec2<double> a, const llmr::vec2<double> b) {
- if (a.y > b.y) {
- return { b.x, b.y, a.x, a.y, a.x - b.x, a.y - b.y };
- } else {
- return { a.x, a.y, b.x, b.y, b.x - a.x, b.y - a.y };
- }
-}
-
-// scan-line conversion
-void _scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
- double y0 = fmax(ymin, floor(e1.y0)),
- y1 = fmin(ymax, ceil(e1.y1));
-
- // sort edges by x-coordinate
- if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ?
- (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) :
- (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) {
- std::swap(e0, e1);
- }
-
- // scan lines!
- double m0 = e0.dx / e0.dy,
- m1 = e1.dx / e1.dy,
- d0 = e0.dx > 0, // use y + 1 to compute x0
- d1 = e1.dx < 0; // use y + 1 to compute x1
- for (int32_t y = y0; y < y1; y++) {
- double x0 = m0 * fmax(0, fmin(e0.dy, y + d0 - e0.y0)) + e0.x0,
- x1 = m1 * fmax(0, fmin(e1.dy, y + d1 - e1.y0)) + e1.x0;
- scanLine(floor(x1), ceil(x0), y, ymax);
- }
-}
-
-// scan-line conversion
-void _scanTriangle(const llmr::vec2<double> a, const llmr::vec2<double> b, const llmr::vec2<double> c, int32_t ymin, int32_t ymax, ScanLine& scanLine) {
- edge ab = _edge(a, b);
- edge bc = _edge(b, c);
- edge ca = _edge(c, a);
-
- // sort edges by y-length
- if (ab.dy > bc.dy) { std::swap(ab, bc); }
- if (ab.dy > ca.dy) { std::swap(ab, ca); }
- if (bc.dy > ca.dy) { std::swap(bc, ca); }
-
- // scan span! scan span!
- if (ab.dy) _scanSpans(ca, ab, ymin, ymax, scanLine);
- if (bc.dy) _scanSpans(ca, bc, ymin, ymax, scanLine);
-}
-
-std::forward_list<llmr::Tile::ID> llmr::covering_tiles(int32_t zoom, const box& points, const bool use_raster, const bool use_retina) {
- int32_t dim = pow(2, zoom);
- std::forward_list<llmr::Tile::ID> tiles;
-
- auto scanLine = [&tiles, zoom, use_raster, use_retina](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
- int32_t x;
- if (y >= 0 && y <= ymax) {
- for (x = x0; x < x1; x++) {
- if (use_raster) {
- uint32_t search_zoom = zoom;
- search_zoom += (uint32_t)((llmr::util::tileSize / 256.0f) - 1.0f);
- if (use_retina) {
- search_zoom += 1;
- }
- Tile::ID id = Tile::ID(zoom, x, y);
- auto ids = id.children(search_zoom);
- for (const Tile::ID& child_id : ids) {
- tiles.emplace_front(child_id.z, child_id.x, child_id.y);
- }
- } else {
- tiles.emplace_front(zoom, x, y);
- }
- }
- }
- };
-
- // Divide the screen up in two triangles and scan each of them:
- // \---+
- // | \ |
- // +---\.
- _scanTriangle(points.tl, points.tr, points.br, 0, dim, scanLine);
- _scanTriangle(points.br, points.bl, points.tl, 0, dim, scanLine);
-
- const vec2<double>& center = points.center;
- tiles.sort([&center](const Tile::ID& a, const Tile::ID& b) {
- // Sorts by distance from the box center
- return fabs(a.x - center.x) + fabs(a.y - center.y) <
- fabs(b.x - center.x) + fabs(b.y - center.y);
- });
-
- tiles.unique();
-
- return tiles;
-}
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 5685102244..48d3df0419 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -1,12 +1,13 @@
#include <llmr/map/map.hpp>
#include <llmr/map/settings.hpp>
+#include <llmr/map/source.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/style/resources.hpp>
#include <llmr/style/sprite.hpp>
-#include <llmr/map/coverage.hpp>
#include <llmr/util/animation.hpp>
#include <algorithm>
+#include <memory>
using namespace llmr;
@@ -18,7 +19,7 @@ Map::Map(Settings& settings)
glyphAtlas(1024, 1024),
painter(transform, settings, style, glyphAtlas),
min_zoom(0),
- max_zoom((use_raster ? kTileRasterMaxZoom : kTileVectorMaxZoom)) {
+ max_zoom(21) {
}
Map::~Map() {
@@ -29,6 +30,32 @@ void Map::setup() {
painter.setup();
style.loadJSON(resources::style, resources::style_size);
+
+ sources.emplace("mapbox streets",
+ Source(*this,
+ transform,
+ painter,
+ texturepool,
+ "http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf",
+ Source::Type::vector,
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14},
+ 512,
+ 0,
+ 14,
+ true));
+
+ sources.emplace("satellite",
+ Source(*this,
+ transform,
+ painter,
+ texturepool,
+ "https://a.tiles.mapbox.com/v3/justin.hh0gkdfm/%d/%d/%d%s.png256",
+ Source::Type::raster,
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21},
+ 256,
+ 0,
+ 21,
+ false));
}
void Map::loadStyle(const uint8_t *const data, uint32_t bytes) {
@@ -53,11 +80,20 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width
}
}
-void Map::toggleRaster() {
- use_raster = ! use_raster;
- max_zoom = (use_raster ? kTileRasterMaxZoom : kTileVectorMaxZoom);
- tiles.clear();
- update();
+box Map::cornersToBox(uint32_t z) const {
+ return transform.cornersToBox(z);
+}
+
+float Map::getPixelRatio() const {
+ return transform.getPixelRatio();
+}
+
+Style& Map::getStyle() {
+ return style;
+}
+
+GlyphAtlas& Map::getGlyphAtlas() {
+ return glyphAtlas;
}
void Map::moveBy(double dx, double dy, double duration) {
@@ -223,6 +259,24 @@ void Map::toggleDebug() {
settings.persist();
}
+void Map::toggleRaster() {
+ auto it = sources.find("satellite");
+ std::pair<std::string, Source&> pair = *it;
+ Source& satellite_source = pair.second;
+
+ if (satellite_source.enabled) {
+ satellite_source.enabled = false;
+ style.appliedClasses.erase(style.appliedClasses.find("satellite"));
+ } else {
+ satellite_source.enabled = true;
+ style.appliedClasses.insert("satellite");
+ }
+
+ style.cascade(transform.getNormalizedZoom());
+
+ update();
+}
+
void Map::cancelAnimations() {
transform.cancelAnimations();
}
@@ -238,197 +292,16 @@ void Map::update() {
platform::restart();
}
-
-TileData::State Map::hasTile(const Tile::ID& id) {
- for (const Tile& tile : tiles) {
- if (tile.id == id && tile.data) {
- return tile.data->state;
- }
- }
-
- return TileData::State::invalid;
-}
-
-TileData::State Map::addTile(const Tile::ID& id) {
- const TileData::State state = hasTile(id);
-
- if (state != TileData::State::invalid) {
- return state;
- }
-
- tiles.emplace_front(id);
- Tile& new_tile = tiles.front();
-
- // We couldn't find the tile in the list. Create a new one.
- // Try to find the associated TileData object.
- const Tile::ID normalized_id = id.normalized();
-
- auto it = std::find_if(tile_data.begin(), tile_data.end(), [&normalized_id](const std::weak_ptr<TileData>& tile_data) {
- return !tile_data.expired() && tile_data.lock()->id == normalized_id;
- });
-
- if (it != tile_data.end()) {
- // Create a shared_ptr handle. Note that this might be empty!
- new_tile.data = it->lock();
- }
-
- if (new_tile.data && new_tile.data->state == TileData::State::obsolete) {
- // Do not consider the tile if it's already obsolete.
- new_tile.data.reset();
- }
-
- if (!new_tile.data) {
- // If we don't find working tile data, we're just going to load it.
- new_tile.data = std::make_shared<TileData>(normalized_id, style, glyphAtlas, use_raster, (pixel_ratio > 1.0));
- new_tile.data->request();
- tile_data.push_front(new_tile.data);
- }
-
- return new_tile.data->state;
-}
-
-/**
- * Recursively find children of the given tile that are already loaded.
- *
- * @param id The tile ID that we should find children for.
- * @param maxCoveringZoom The maximum zoom level of children to look for.
- * @param retain An object that we add the found tiles to.
- *
- * @return boolean Whether the children found completely cover the tile.
- */
-bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain) {
- bool complete = true;
- int32_t z = id.z;
-
-
- auto ids = id.children(z + 1);
- for (const Tile::ID& child_id : ids) {
- const TileData::State state = hasTile(child_id);
- if (state == TileData::State::parsed) {
- retain.emplace_front(child_id);
- } else {
- complete = false;
- if (z < maxCoveringZoom) {
- // Go further down the hierarchy to find more unloaded children.
- findLoadedChildren(child_id, maxCoveringZoom, retain);
- }
- }
- }
- return complete;
-}
-
-/**
- * Find a loaded parent of the given tile.
- *
- * @param id The tile ID that we should find children for.
- * @param minCoveringZoom The minimum zoom level of parents to look for.
- * @param retain An object that we add the found tiles to.
- *
- * @return boolean Whether a parent was found.
- */
-bool Map::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain) {
- for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
- const Tile::ID parent_id = id.parent(z);
- const TileData::State state = hasTile(parent_id);
- if (state == TileData::State::parsed) {
- retain.emplace_front(parent_id);
- return true;
- }
- }
- return false;
-}
-
-
bool Map::updateTiles() {
bool changed = false;
- // Figure out what tiles we need to load
- int32_t zoom = transform.getZoom();
- if (zoom > max_zoom) zoom = max_zoom;
- if (zoom < min_zoom) zoom = min_zoom;
-
- int32_t max_covering_zoom = zoom + 1;
- if (max_covering_zoom > max_zoom) max_covering_zoom = max_zoom;
-
- int32_t min_covering_zoom = zoom - 10;
- if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom;
-
- int32_t max_dim = pow(2, zoom);
-
- // Map four viewport corners to pixel coordinates
- box box = transform.mapCornersToBox(zoom);
-
- // Performs a scanline algorithm search that covers the rectangle of the box
- // and sorts them by proximity to the center.
- std::forward_list<Tile::ID> required = llmr::covering_tiles(zoom, box, use_raster, (pixel_ratio > 1.0));
-
- // Retain is a list of tiles that we shouldn't delete, even if they are not
- // the most ideal tile for the current viewport. This may include tiles like
- // parent or child tiles that are *already* loaded.
- std::forward_list<Tile::ID> retain(required);
-
- // Add existing child/parent tiles if the actual tile is not yet loaded
- for (const Tile::ID& id : required) {
- const TileData::State state = addTile(id);
-
- if (state != TileData::State::parsed) {
- if (use_raster && (transform.rotating || transform.scaling || transform.panning))
- break;
-
- // The tile we require is not yet loaded. Try to find a parent or
- // child tile that we already have.
-
- // First, try to find existing child tiles that completely cover the
- // missing tile.
- bool complete = findLoadedChildren(id, max_covering_zoom, retain);
-
- // Then, if there are no complete child tiles, try to find existing
- // parent tiles that completely cover the missing tile.
- if (!complete) {
- findLoadedParent(id, min_covering_zoom, retain);
- }
- }
-
- if (state == TileData::State::initial) {
- changed = true;
+ for (std::pair<std::string, Source&> pair : sources) {
+ Source& source = pair.second;
+ if (source.enabled) {
+ changed = source.update() || changed;
}
}
- // Remove tiles that we definitely don't need, i.e. tiles that are not on
- // the required list.
- std::forward_list<Tile::ID> retain_data;
- tiles.remove_if([&retain, &retain_data, &changed](const Tile & tile) {
- bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end();
- if (obsolete) {
- changed = true;
- } else {
- retain_data.push_front(tile.data->id);
- }
- return obsolete;
- });
-
- // Sort tiles by zoom level, front to back.
- // We're painting front-to-back, so we want to draw more detailed tiles first
- // before filling in other parts with lower zoom levels.
- tiles.sort([](const Tile & a, const Tile & b) {
- return a.id.z > b.id.z;
- });
-
- // Remove all the expired pointers from the list.
- tile_data.remove_if([&retain_data](const std::weak_ptr<TileData>& tile_data) {
- const std::shared_ptr<TileData> tile = tile_data.lock();
- if (!tile) {
- return true;
- }
- bool obsolete = std::find(retain_data.begin(), retain_data.end(), tile->id) == retain_data.end();
- if (obsolete) {
- tile->cancel();
- return true;
- } else {
- return false;
- }
- });
-
return changed;
}
@@ -452,32 +325,31 @@ bool Map::render() {
painter.changeMatrix();
- // First, update all tile matrices with the new transform and render into
- // the stencil buffer.
- uint8_t i = 1;
+ // First, update the sources' tile matrices with the new
+ // transform and render into the stencil buffer, including
+ // drawing the background.
+
painter.prepareClippingMask();
- for (Tile& tile : tiles) {
- if (tile.data && tile.data->state == TileData::State::parsed) {
- // The position matrix.
- transform.matrixFor(tile.matrix, tile.id);
- matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
- tile.clip_id = i++;
- painter.drawClippingMask(tile.matrix, tile.clip_id, !tile.data->use_raster);
- }
+
+ bool is_baselayer = true;
+
+ for (std::pair<std::string, Source&> pair : sources) {
+ Source& source = pair.second;
+ source.prepare_render(is_baselayer);
+ is_baselayer = source.enabled ? false : (true && is_baselayer);
}
+
painter.finishClippingMask();
- for (const Tile& tile : tiles) {
- if (tile.data && tile.data->state == TileData::State::parsed) {
- if (tile.data->use_raster && *tile.data->raster && !tile.data->raster->textured) {
- tile.data->raster->setTexturepool(&texturepool);
- tile.data->raster->beginFadeInAnimation();
- }
- if (tile.data->use_raster && tile.data->raster->needsAnimation()) {
- tile.data->raster->updateAnimations(animationTime);
- }
- painter.render(tile);
- }
+ // Then, render each source's tiles. TODO: handle more than one
+ // vector source.
+
+ is_baselayer = true;
+
+ for (std::pair<std::string, Source&> pair : sources) {
+ Source& source = pair.second;
+ source.render(animationTime, is_baselayer);
+ is_baselayer = source.enabled ? false : (true && is_baselayer);
}
painter.renderMatte();
diff --git a/src/map/source.cpp b/src/map/source.cpp
new file mode 100644
index 0000000000..87e6a021b1
--- /dev/null
+++ b/src/map/source.cpp
@@ -0,0 +1,379 @@
+#include <llmr/map/source.hpp>
+#include <llmr/map/map.hpp>
+#include <llmr/map/transform.hpp>
+#include <llmr/renderer/painter.hpp>
+#include <llmr/util/constants.hpp>
+#include <llmr/util/raster.hpp>
+#include <llmr/util/string.hpp>
+#include <llmr/util/texturepool.hpp>
+#include <llmr/util/vec.hpp>
+#include <llmr/geometry/glyph_atlas.hpp>
+
+using namespace llmr;
+
+Source::Source(Map& map, Transform& transform, Painter& painter, Texturepool& texturepool, const char *url, Source::Type type, std::list<uint32_t> zooms, uint32_t tile_size, uint32_t min_zoom, uint32_t max_zoom, bool enabled)
+ : map(map),
+ transform(transform),
+ painter(painter),
+ texturepool(texturepool),
+ url(url),
+ type(type),
+ zooms(zooms),
+ tile_size(tile_size),
+ min_zoom(min_zoom),
+ max_zoom(max_zoom),
+ enabled(enabled) {
+}
+
+bool Source::update() {
+ return updateTiles();
+}
+
+void Source::prepare_render(bool is_baselayer) {
+ if (!enabled) return;
+
+ uint8_t i = 1;
+
+ for (Tile& tile : tiles) {
+ if (tile.data && tile.data->state == TileData::State::parsed) {
+ transform.matrixFor(tile.matrix, tile.id);
+ matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
+ tile.clip_id = i++;
+ if (is_baselayer) {
+ painter.drawClippingMask(tile.matrix, tile.clip_id);
+ }
+ }
+ }
+}
+
+void Source::render(double animationTime, bool is_baselayer) {
+ if (!enabled) return;
+
+ for (const Tile& tile : tiles) {
+ if (tile.data && tile.data->state == TileData::State::parsed) {
+ if (type == Type::raster) {
+ std::shared_ptr<Raster> raster = tile.data->raster;
+ if (raster && !raster->textured) {
+ raster->setTexturepool(&texturepool);
+ raster->beginFadeInAnimation();
+ }
+ if (raster && raster->needsAnimation()) {
+ raster->updateAnimations(animationTime);
+ }
+ }
+ painter.render(tile);
+ }
+ }
+}
+
+TileData::State Source::hasTile(const Tile::ID& id) {
+ for (const Tile& tile : tiles) {
+ if (tile.id == id && tile.data) {
+ return tile.data->state;
+ }
+ }
+
+ return TileData::State::invalid;
+}
+
+TileData::State Source::addTile(const Tile::ID& id) {
+ const TileData::State state = hasTile(id);
+
+ if (state != TileData::State::invalid) {
+ return state;
+ }
+
+ tiles.emplace_front(id);
+ Tile& new_tile = tiles.front();
+
+ // We couldn't find the tile in the list. Create a new one.
+ // Try to find the associated TileData object.
+ const Tile::ID normalized_id = id.normalized();
+
+ auto it = std::find_if(tile_data.begin(), tile_data.end(), [normalized_id](const std::weak_ptr<TileData>& tile_data) {
+ return !tile_data.expired() && tile_data.lock()->id == normalized_id;
+ });
+
+ if (it != tile_data.end()) {
+ // Create a shared_ptr handle. Note that this might be empty!
+ new_tile.data = it->lock();
+ }
+
+ if (new_tile.data && new_tile.data->state == TileData::State::obsolete) {
+ // Do not consider the tile if it's already obsolete.
+ new_tile.data.reset();
+ }
+
+ if (!new_tile.data) {
+ // If we don't find working tile data, we're just going to load it.
+
+ std::string formed_url;
+
+ if (type == Source::Type::vector) {
+ formed_url = util::sprintf(url, id.z, id.x, id.y);
+ } else {
+ formed_url = util::sprintf(url, id.z, id.x, id.y, (map.getPixelRatio() > 1.0 ? "@2x" : ""));
+ }
+
+ new_tile.data = std::make_shared<TileData>(normalized_id, map.getStyle(), map.getGlyphAtlas(), formed_url, (type == Source::Type::raster));
+ new_tile.data->request();
+ tile_data.push_front(new_tile.data);
+ }
+
+ return new_tile.data->state;
+}
+
+/**
+ * Recursively find children of the given tile that are already loaded.
+ *
+ * @param id The tile ID that we should find children for.
+ * @param maxCoveringZoom The maximum zoom level of children to look for.
+ * @param retain An object that we add the found tiles to.
+ *
+ * @return boolean Whether the children found completely cover the tile.
+ */
+bool Source::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain) {
+ bool complete = true;
+ int32_t z = id.z;
+
+
+ auto ids = id.children(z + 1);
+ for (const Tile::ID& child_id : ids) {
+ const TileData::State state = hasTile(child_id);
+ if (state == TileData::State::parsed) {
+ retain.emplace_front(child_id);
+ } else {
+ complete = false;
+ if (z < maxCoveringZoom) {
+ // Go further down the hierarchy to find more unloaded children.
+ findLoadedChildren(child_id, maxCoveringZoom, retain);
+ }
+ }
+ }
+ return complete;
+}
+
+/**
+ * Find a loaded parent of the given tile.
+ *
+ * @param id The tile ID that we should find children for.
+ * @param minCoveringZoom The minimum zoom level of parents to look for.
+ * @param retain An object that we add the found tiles to.
+ *
+ * @return boolean Whether a parent was found.
+ */
+bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain) {
+ for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
+ const Tile::ID parent_id = id.parent(z);
+ const TileData::State state = hasTile(parent_id);
+ if (state == TileData::State::parsed) {
+ retain.emplace_front(parent_id);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Source::updateTiles() {
+ bool changed = false;
+
+ // Figure out what tiles we need to load
+ int32_t zoom = map.getZoom();
+ if (zoom > max_zoom) zoom = max_zoom;
+ if (zoom < min_zoom) zoom = min_zoom;
+
+ int32_t max_covering_zoom = zoom + 1;
+ if (max_covering_zoom > max_zoom) max_covering_zoom = max_zoom;
+
+ int32_t min_covering_zoom = zoom - 10;
+ if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom;
+
+ int32_t max_dim = pow(2, zoom);
+
+ // Map four viewport corners to pixel coordinates
+ box box = map.cornersToBox(zoom);
+
+ // Performs a scanline algorithm search that covers the rectangle of the box
+ // and sorts them by proximity to the center.
+
+ std::forward_list<Tile::ID> required = covering_tiles(zoom, box);
+
+ // Retain is a list of tiles that we shouldn't delete, even if they are not
+ // the most ideal tile for the current viewport. This may include tiles like
+ // parent or child tiles that are *already* loaded.
+ std::forward_list<Tile::ID> retain(required);
+
+ // Add existing child/parent tiles if the actual tile is not yet loaded
+ for (const Tile::ID& id : required) {
+ const TileData::State state = addTile(id);
+
+ if (state != TileData::State::parsed) {
+// if (use_raster && (transform.rotating || transform.scaling || transform.panning))
+// break;
+
+ // The tile we require is not yet loaded. Try to find a parent or
+ // child tile that we already have.
+
+ // First, try to find existing child tiles that completely cover the
+ // missing tile.
+ bool complete = findLoadedChildren(id, max_covering_zoom, retain);
+
+ // Then, if there are no complete child tiles, try to find existing
+ // parent tiles that completely cover the missing tile.
+ if (!complete) {
+ findLoadedParent(id, min_covering_zoom, retain);
+ }
+ }
+
+ if (state == TileData::State::initial) {
+ changed = true;
+ }
+ }
+
+ // Remove tiles that we definitely don't need, i.e. tiles that are not on
+ // the required list.
+ std::forward_list<Tile::ID> retain_data;
+ tiles.remove_if([&retain, &retain_data, &changed](const Tile & tile) {
+ bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end();
+ if (obsolete) {
+ changed = true;
+ } else {
+ retain_data.push_front(tile.data->id);
+ }
+ return obsolete;
+ });
+
+ // Sort tiles by zoom level, front to back.
+ // We're painting front-to-back, so we want to draw more detailed tiles first
+ // before filling in other parts with lower zoom levels.
+ tiles.sort([](const Tile & a, const Tile & b) {
+ return a.id.z > b.id.z;
+ });
+
+ // Remove all the expired pointers from the list.
+ tile_data.remove_if([&retain_data](const std::weak_ptr<TileData>& tile_data) {
+ const std::shared_ptr<TileData> tile = tile_data.lock();
+ if (!tile) {
+ return true;
+ }
+ bool obsolete = std::find(retain_data.begin(), retain_data.end(), tile->id) == retain_data.end();
+ if (obsolete) {
+ tile->cancel();
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ return changed;
+}
+
+// Taken from polymaps src/Layer.js
+// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
+
+struct edge {
+ double x0 = 0, y0 = 0;
+ double x1 = 0, y1 = 0;
+ double dx = 0, dy = 0;
+ edge(double x0, double y0, double x1, double y1, double dx, double dy)
+ : x0(x0), y0(y0), x1(x1), y1(y1), dx(dx), dy(dy) {}
+};
+
+typedef const std::function<void(int32_t, int32_t, int32_t, int32_t)> ScanLine;
+
+// scan-line conversion
+edge _edge(const llmr::vec2<double> a, const llmr::vec2<double> b) {
+ if (a.y > b.y) {
+ return { b.x, b.y, a.x, a.y, a.x - b.x, a.y - b.y };
+ } else {
+ return { a.x, a.y, b.x, b.y, b.x - a.x, b.y - a.y };
+ }
+}
+
+// scan-line conversion
+void _scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
+ double y0 = fmax(ymin, floor(e1.y0)),
+ y1 = fmin(ymax, ceil(e1.y1));
+
+ // sort edges by x-coordinate
+ if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ?
+ (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) :
+ (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) {
+ std::swap(e0, e1);
+ }
+
+ // scan lines!
+ double m0 = e0.dx / e0.dy,
+ m1 = e1.dx / e1.dy,
+ d0 = e0.dx > 0, // use y + 1 to compute x0
+ d1 = e1.dx < 0; // use y + 1 to compute x1
+ for (int32_t y = y0; y < y1; y++) {
+ double x0 = m0 * fmax(0, fmin(e0.dy, y + d0 - e0.y0)) + e0.x0,
+ x1 = m1 * fmax(0, fmin(e1.dy, y + d1 - e1.y0)) + e1.x0;
+ scanLine(floor(x1), ceil(x0), y, ymax);
+ }
+}
+
+// scan-line conversion
+void _scanTriangle(const llmr::vec2<double> a, const llmr::vec2<double> b, const llmr::vec2<double> c, int32_t ymin, int32_t ymax, ScanLine& scanLine) {
+ edge ab = _edge(a, b);
+ edge bc = _edge(b, c);
+ edge ca = _edge(c, a);
+
+ // sort edges by y-length
+ if (ab.dy > bc.dy) { std::swap(ab, bc); }
+ if (ab.dy > ca.dy) { std::swap(ab, ca); }
+ if (bc.dy > ca.dy) { std::swap(bc, ca); }
+
+ // scan span! scan span!
+ if (ab.dy) _scanSpans(ca, ab, ymin, ymax, scanLine);
+ if (bc.dy) _scanSpans(ca, bc, ymin, ymax, scanLine);
+}
+
+double Source::getZoom() const {
+ double offset = log(util::tileSize / tile_size) / log(2);
+ offset += (map.getPixelRatio() > 1.0 ? 1 :0);
+ return map.getZoom() + offset;
+}
+
+std::forward_list<llmr::Tile::ID> Source::covering_tiles(int32_t zoom, const box& points) {
+ int32_t dim = pow(2, zoom);
+ std::forward_list<llmr::Tile::ID> tiles;
+ bool is_raster = (type == Type::raster);
+ double search_zoom = getZoom();
+
+ auto scanLine = [&tiles, zoom, is_raster, search_zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
+ int32_t x;
+ if (y >= 0 && y <= ymax) {
+ for (x = x0; x < x1; x++) {
+ if (is_raster) {
+ Tile::ID id = Tile::ID(zoom, x, y);
+ auto ids = id.children(search_zoom);
+ for (const Tile::ID& child_id : ids) {
+ tiles.emplace_front(child_id.z, child_id.x, child_id.y);
+ }
+ } else {
+ tiles.emplace_front(zoom, x, y);
+ }
+ }
+ }
+ };
+
+ // Divide the screen up in two triangles and scan each of them:
+ // \---+
+ // | \ |
+ // +---\.
+ _scanTriangle(points.tl, points.tr, points.br, 0, dim, scanLine);
+ _scanTriangle(points.br, points.bl, points.tl, 0, dim, scanLine);
+
+ const vec2<double>& center = points.center;
+ tiles.sort([&center](const Tile::ID& a, const Tile::ID& b) {
+ // Sorts by distance from the box center
+ return fabs(a.x - center.x) + fabs(a.y - center.y) <
+ fabs(b.x - center.x) + fabs(b.y - center.y);
+ });
+
+ tiles.unique();
+
+ return tiles;
+}
diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp
index 4df01778d1..203b147c89 100644
--- a/src/map/tile_data.cpp
+++ b/src/map/tile_data.cpp
@@ -11,11 +11,11 @@
using namespace llmr;
-TileData::TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas, const bool use_raster, const bool use_retina)
+TileData::TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas, const std::string url, const bool is_raster)
: id(id),
- use_raster(use_raster),
- use_retina(use_retina),
+ url(url),
state(State::initial),
+ is_raster(is_raster),
raster(),
style(style),
glyphAtlas(glyphAtlas) {
@@ -38,17 +38,9 @@ const std::string TileData::toString() const {
void TileData::request() {
state = State::loading;
- std::string url;
-
- if (use_raster) {
- url = util::sprintf(kTileRasterURL, id.z, id.x, id.y, (use_retina ? "@2x" : ""));
- } else {
- url = util::sprintf(kTileVectorURL, id.z, id.x, id.y);
- }
-
// Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
- req = platform::request_http(url, [weak_tile, url](platform::Response *res) {
+ req = platform::request_http(url, [weak_tile](platform::Response *res) {
std::shared_ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
@@ -58,7 +50,7 @@ void TileData::request() {
tile->data.swap(res->body);
tile->parse();
} else {
- fprintf(stderr, "[%s] tile loading failed: %d, %s\n", url.c_str(), res->code, res->error_message.c_str());
+ fprintf(stderr, "[%s] tile loading failed: %d, %s\n", tile->url.c_str(), res->code, res->error_message.c_str());
}
}, []() {
platform::restart();
@@ -77,18 +69,11 @@ bool TileData::parse() {
return false;
}
- if (use_raster) {
- raster = std::make_shared<Raster>();
- raster->load(data);
- state = State::parsed;
- return true;
- }
-
try {
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
- TileParser parser(data, *this, style, glyphAtlas);
+ TileParser parser(data, *this, style, glyphAtlas, is_raster);
} catch (const std::exception& ex) {
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
cancel();
diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp
index c21b6b961b..9e75fe3f70 100644
--- a/src/map/tile_parser.cpp
+++ b/src/map/tile_parser.cpp
@@ -5,19 +5,22 @@
#include <llmr/renderer/line_bucket.hpp>
#include <llmr/renderer/point_bucket.hpp>
#include <llmr/renderer/text_bucket.hpp>
+#include <llmr/renderer/raster_bucket.hpp>
+#include <llmr/util/raster.hpp>
#include <llmr/util/std.hpp>
using namespace llmr;
-TileParser::TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas)
- : data(pbf((const uint8_t *)data.data(), data.size())),
+TileParser::TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas, bool is_raster)
+ : vector_data(is_raster ? pbf(0, 0) : pbf((const uint8_t *)data.data(), data.size())),
+ raster_data(is_raster ? data : ""),
tile(tile),
style(style),
glyphAtlas(glyphAtlas),
- placement(tile.id.z) {
- parseGlyphs();
+ placement(is_raster ? 0 : tile.id.z) {
+ if (!is_raster) parseGlyphs();
parseStyleLayers(style.layers);
}
@@ -26,7 +29,7 @@ bool TileParser::obsolete() const {
}
void TileParser::parseGlyphs() {
- for (const std::pair<std::string, const VectorTileFace> pair : data.faces) {
+ for (const std::pair<std::string, const VectorTileFace> pair : vector_data.faces) {
const std::string &name = pair.first;
const VectorTileFace &face = pair.second;
@@ -77,22 +80,30 @@ void TileParser::parseStyleLayers(const std::vector<LayerDescription>& layers) {
}
std::unique_ptr<Bucket> TileParser::createBucket(const BucketDescription& bucket_desc) {
- auto layer_it = data.layers.find(bucket_desc.source_layer);
- if (layer_it != data.layers.end()) {
- const VectorTileLayer& layer = layer_it->second;
- if (bucket_desc.type == BucketType::Fill) {
- return createFillBucket(layer, bucket_desc);
- } else if (bucket_desc.type == BucketType::Line) {
- return createLineBucket(layer, bucket_desc);
- } else if (bucket_desc.type == BucketType::Point) {
- return createPointBucket(layer, bucket_desc);
- } else if (bucket_desc.type == BucketType::Text) {
- return createTextBucket(layer, bucket_desc);
- } else {
- throw std::runtime_error("unknown bucket type");
+ if (bucket_desc.type == BucketType::Raster) {
+ if (raster_data.length()) {
+ tile.raster = std::make_shared<Raster>();
+ tile.raster->load(raster_data);
}
+ return createRasterBucket(bucket_desc);
} else {
- // The layer specified in the bucket does not exist. Do nothing.
+ auto layer_it = vector_data.layers.find(bucket_desc.source_layer);
+ if (layer_it != vector_data.layers.end()) {
+ const VectorTileLayer& layer = layer_it->second;
+ if (bucket_desc.type == BucketType::Fill) {
+ return createFillBucket(layer, bucket_desc);
+ } else if (bucket_desc.type == BucketType::Line) {
+ return createLineBucket(layer, bucket_desc);
+ } else if (bucket_desc.type == BucketType::Point) {
+ return createPointBucket(layer, bucket_desc);
+ } else if (bucket_desc.type == BucketType::Text) {
+ return createTextBucket(layer, bucket_desc);
+ } else {
+ throw std::runtime_error("unknown bucket type");
+ }
+ } else {
+ // The layer specified in the bucket does not exist. Do nothing.
+ }
}
return nullptr;
@@ -166,3 +177,10 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& laye
return std::move(bucket);
}
+
+std::unique_ptr<Bucket> TileParser::createRasterBucket(const BucketDescription& bucket_desc) {
+ std::unique_ptr<RasterBucket> bucket = std::make_unique<RasterBucket>(bucket_desc);
+ // Raster buckets are just empty dummies so that they behave
+ // similarly to vector buckets in styling configurations.
+ return obsolete() ? nullptr : std::move(bucket);
+}
diff --git a/src/map/transform.cpp b/src/map/transform.cpp
index 3c9246adaa..545de6e805 100644
--- a/src/map/transform.cpp
+++ b/src/map/transform.cpp
@@ -298,7 +298,7 @@ double Transform::getAngle() const {
return angle;
}
-box Transform::mapCornersToBox(uint32_t z) const {
+box Transform::cornersToBox(uint32_t z) const {
const double ref_scale = pow(2, z);
const double angle_sin = sin(-angle);
diff --git a/src/platform/platform.cpp b/src/platform/platform.cpp
index 78381b8d81..6f36105462 100644
--- a/src/platform/platform.cpp
+++ b/src/platform/platform.cpp
@@ -1,8 +1,3 @@
#include <llmr/platform/platform.hpp>
-const char *llmr::kTileVectorURL = "http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf";
-const char *llmr::kTileRasterURL = "http://a.tiles.mapbox.com/v3/kkaefer.holbci77/%d/%d/%d.png";
const char *llmr::kSpriteURL = "http://mapbox-kkaefer.s3.amazonaws.com/static/sprite";
-
-const int32_t llmr::kTileVectorMaxZoom = 14;
-const int32_t llmr::kTileRasterMaxZoom = 21;
diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp
index 6afa474a8c..fb2cd0e342 100644
--- a/src/renderer/painter.cpp
+++ b/src/renderer/painter.cpp
@@ -9,6 +9,7 @@
#include <llmr/renderer/line_bucket.hpp>
#include <llmr/renderer/point_bucket.hpp>
#include <llmr/renderer/text_bucket.hpp>
+#include <llmr/renderer/raster_bucket.hpp>
#include <llmr/map/transform.hpp>
#include <llmr/map/settings.hpp>
@@ -123,13 +124,13 @@ void Painter::prepareClippingMask() {
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
}
-void Painter::drawClippingMask(const mat4& matrix, uint8_t clip_id, bool opaque) {
+void Painter::drawClippingMask(const mat4& matrix, uint8_t clip_id) {
plainShader->setMatrix(matrix);
- if (opaque) {
- plainShader->setColor(style.computed.background.color);
- }
- glStencilFunc(GL_ALWAYS, clip_id, 0xFF);
+ plainShader->setColor(style.computed.background.color);
+ plainShader->setOpacity(style.computed.background.opacity);
+
+ glStencilFunc(GL_ALWAYS, 1, 0xFF);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
}
@@ -160,31 +161,14 @@ void Painter::render(const Tile& tile) {
frameHistory.record(transform.getNormalizedZoom());
matrix = tile.matrix;
- glStencilFunc(GL_EQUAL, tile.clip_id, 0xFF);
- if (tile.data->use_raster) {
- renderRaster(tile.data);
- } else {
- renderLayers(tile.data, style.layers);
- }
+ renderLayers(tile.data, style.layers);
if (settings.debug) {
renderDebug(tile.data);
}
}
-void Painter::renderRaster(const std::shared_ptr<TileData>& tile_data) {
- useProgram(rasterShader->program);
- rasterShader->setMatrix(matrix);
- rasterShader->setImage(0);
- rasterShader->setOpacity(tile_data->raster->opacity);
- tile_data->raster->bind(true);
-
- coveringRasterArray.bind(*rasterShader, tileStencilBuffer, BUFFER_OFFSET(0));
- glDepthRange(strata, 1.0f);
- glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
-}
-
void Painter::renderLayers(const std::shared_ptr<TileData>& tile_data, const std::vector<LayerDescription>& layers) {
float strata_thickness = 1.0f / (layers.size() + 1);
@@ -226,14 +210,37 @@ void Painter::renderLayer(const std::shared_ptr<TileData>& tile_data, const Laye
} else {
// This is a singular layer. Try to find the bucket associated with
// this layer and render it.
- auto bucket_it = tile_data->buckets.find(layer_desc.bucket_name);
- if (bucket_it != tile_data->buckets.end()) {
- assert(bucket_it->second);
- bucket_it->second->render(*this, layer_desc.name, tile_data->id);
+ if (style.buckets[layer_desc.bucket_name].type == BucketType::Raster) {
+ if (tile_data && tile_data->raster) {
+ renderRaster(layer_desc.name, tile_data);
+ }
+ } else {
+ auto bucket_it = tile_data->buckets.find(layer_desc.bucket_name);
+ if (bucket_it != tile_data->buckets.end()) {
+ assert(bucket_it->second);
+ bucket_it->second->render(*this, layer_desc.name, tile_data->id);
+ }
}
}
}
+void Painter::renderRaster(const std::string& layer_name, const std::shared_ptr<TileData>& tile_data) {
+ if (pass == Opaque) return;
+
+ const RasterProperties& properties = style.computed.rasters[layer_name];
+ if (!properties.enabled) return;
+
+ useProgram(rasterShader->program);
+ rasterShader->setMatrix(matrix);
+ rasterShader->setImage(0);
+ rasterShader->setOpacity(properties.opacity * tile_data->raster->opacity);
+ tile_data->raster->bind(true);
+
+ coveringRasterArray.bind(*rasterShader, tileStencilBuffer, BUFFER_OFFSET(0));
+ glDepthRange(strata, 1.0f);
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
+}
+
void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
// Abort early.
if (bucket.empty()) return;
diff --git a/src/renderer/raster_bucket.cpp b/src/renderer/raster_bucket.cpp
new file mode 100644
index 0000000000..54f7dc6398
--- /dev/null
+++ b/src/renderer/raster_bucket.cpp
@@ -0,0 +1,14 @@
+#include <llmr/renderer/raster_bucket.hpp>
+#include <llmr/renderer/painter.hpp>
+
+using namespace llmr;
+
+RasterBucket::RasterBucket(const BucketDescription& bucket_desc) {
+}
+
+void RasterBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
+ // The painter renders rasters directly. This function is a no-op
+ // and exists just to satisfy the abstract superclass requirements.
+ // That way, we can avoid having raster buckets pass around
+ // references to tile data and/or rasters directly.
+}
diff --git a/src/shader/plain.fragment.glsl b/src/shader/plain.fragment.glsl
index 8df552c171..0761ea5e65 100644
--- a/src/shader/plain.fragment.glsl
+++ b/src/shader/plain.fragment.glsl
@@ -1,5 +1,6 @@
uniform vec4 u_color;
+uniform float u_opacity;
void main() {
- gl_FragColor = u_color;
+ gl_FragColor = u_color * u_opacity;
}
diff --git a/src/shader/plain_shader.cpp b/src/shader/plain_shader.cpp
index ac4b7edeb0..c9bb3e59d6 100644
--- a/src/shader/plain_shader.cpp
+++ b/src/shader/plain_shader.cpp
@@ -20,11 +20,13 @@ PlainShader::PlainShader()
u_matrix = glGetUniformLocation(program, "u_matrix");
u_color = glGetUniformLocation(program, "u_color");
+ u_opacity = glGetUniformLocation(program, "u_opacity");
// fprintf(stderr, "PlainShader:\n");
// fprintf(stderr, " - a_pos: %d\n", a_pos);
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
// fprintf(stderr, " - u_color: %d\n", u_color);
+ // fprintf(stderr, " - u_opacity: %f\n", u_opacity);
}
void PlainShader::bind(char *offset) {
@@ -42,3 +44,10 @@ void PlainShader::setColor(const std::array<float, 4>& new_color) {
void PlainShader::setColor(float r, float g, float b, float a) {
setColor({{ r, g, b, a }});
}
+
+void PlainShader::setOpacity(float new_opacity) {
+ if (opacity != new_opacity) {
+ glUniform1f(u_opacity, new_opacity);
+ opacity = new_opacity;
+ }
+}
diff --git a/src/shader/shaders_gl.cpp b/src/shader/shaders_gl.cpp
index 5bc95082d3..bde4fa65c6 100644
--- a/src/shader/shaders_gl.cpp
+++ b/src/shader/shaders_gl.cpp
@@ -24,7 +24,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
},
{
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
- "#version 120\nuniform vec4 u_color;\nvoid main ()\n{\n gl_FragColor = u_color;\n}\n\n",
+ "#version 120\nuniform vec4 u_color;\nuniform float u_opacity;\nvoid main ()\n{\n gl_FragColor = (u_color * u_opacity);\n}\n\n",
},
{
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
diff --git a/src/shader/shaders_gles2.cpp b/src/shader/shaders_gles2.cpp
index e30b650bd1..e6a174da59 100644
--- a/src/shader/shaders_gles2.cpp
+++ b/src/shader/shaders_gles2.cpp
@@ -24,7 +24,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
},
{
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
- "precision highp float;\nuniform vec4 u_color;\nvoid main ()\n{\n gl_FragColor = u_color;\n}\n\n",
+ "precision highp float;\nuniform vec4 u_color;\nuniform float u_opacity;\nvoid main ()\n{\n gl_FragColor = (u_color * u_opacity);\n}\n\n",
},
{
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
diff --git a/src/style/style.cpp b/src/style/style.cpp
index 7e72623f9f..ad85c7f1a7 100644
--- a/src/style/style.cpp
+++ b/src/style/style.cpp
@@ -76,11 +76,12 @@ void Style::cascade(float z) {
point.image = layer.image;
}
+ // Cascade text classes
for (const auto& text_pair : sheetClass.text) {
const std::string& layer_name = text_pair.first;
const llmr::TextClass& layer = text_pair.second;
- // TODO: This should be restricted to point styles that have actual
+ // TODO: This should be restricted to text styles that have actual
// values so as to not override with default values.
llmr::TextProperties& text = computed.texts[layer_name];
text.enabled = layer.enabled.evaluate<bool>(z);
@@ -92,8 +93,21 @@ void Style::cascade(float z) {
text.alwaysVisible = layer.alwaysVisible.evaluate<bool>(z);
}
+ // Cascade raster classes
+ for (const auto& raster_pair : sheetClass.raster) {
+ const std::string& layer_name = raster_pair.first;
+ const llmr::RasterClass& layer = raster_pair.second;
+
+ // TODO: This should be restricted to raster styles that have actual
+ // values so as to not override with default values.
+ llmr::RasterProperties& raster = computed.rasters[layer_name];
+ raster.enabled = layer.enabled.evaluate<bool>(z);
+ raster.opacity = layer.opacity.evaluate<float>(z);
+ }
+
// Cascade background
computed.background.color = sheetClass.background.color;
+ computed.background.opacity = sheetClass.background.opacity.evaluate<float>(z);
}
}
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index 7c452f94da..9064b3ac69 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -254,10 +254,12 @@ void StyleParser::parseClass(const std::string& name, JSVal value, ClassDescript
class_desc.point.insert({ name, std::forward<PointClass>(parsePointClass(value)) });
} else if (type_name == "text") {
class_desc.text.insert({ name, std::forward<TextClass>(parseTextClass(value)) });
+ } else if (type_name == "raster") {
+ class_desc.raster.insert({ name, std::forward<RasterClass>(parseRasterClass(value)) });
} else if (type_name == "background") {
class_desc.background = parseBackgroundClass(value);
} else {
- throw Style::exception("unkonwn class type name");
+ throw Style::exception("unknown class type name");
}
} else {
throw Style::exception("style class type must be a string");
@@ -502,6 +504,20 @@ TextClass StyleParser::parseTextClass(JSVal value) {
return klass;
}
+RasterClass StyleParser::parseRasterClass(JSVal value) {
+ RasterClass klass;
+
+ if (value.HasMember("enabled")) {
+ klass.enabled = parseFunction(value["enabled"]);
+ }
+
+ if (value.HasMember("opacity")) {
+ klass.opacity = parseFunction(value["opacity"]);
+ }
+
+ return klass;
+}
+
BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
BackgroundClass klass;
@@ -509,6 +525,10 @@ BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
klass.color = parseColor(value["color"]);
}
+ if (value.HasMember("opacity")) {
+ klass.opacity = parseFunction(value["opacity"]);
+ }
+
return klass;
}