Merge branch 'master' into style-parser-test
Conflicts: bin/build-style.js bin/default.style.json mapboxgl.gyp src/style/style_parser.cpp test/test.gyp
This commit is contained in:
@@ -22,6 +22,7 @@ before_install:
|
||||
|
||||
install:
|
||||
- make setup
|
||||
- npm install git+https://github.com/mapbox/mapbox-gl-test-suite.git
|
||||
|
||||
before_script:
|
||||
- rm -rf mapnik-packaging/out/packages
|
||||
@@ -30,10 +31,7 @@ script:
|
||||
- make linux -j4 BUILDTYPE=${BUILDTYPE}
|
||||
- make test -j4 BUILDTYPE=${BUILDTYPE}
|
||||
- ./scripts/run_tests.sh
|
||||
- ./scripts/compare_images.js
|
||||
|
||||
after_script:
|
||||
- ./scripts/deploy_results.sh
|
||||
- (cd ./node_modules/mapbox-gl-test-suite/ && (./bin/compare_images.js; ./bin/deploy_results.sh))
|
||||
|
||||
notifications:
|
||||
hipchat:
|
||||
|
||||
4
Makefile
4
Makefile
@@ -78,8 +78,10 @@ xproj: config.gypi macosx/mapboxgl-app.gyp clear_xcode_cache node
|
||||
open ./build/macosx/mapboxgl-app.xcodeproj
|
||||
|
||||
# build iOS project for Xcode
|
||||
iproj: config.gypi ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp clear_xcode_cache node
|
||||
iproj-cli: config.gypi ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp clear_xcode_cache node
|
||||
deps/run_gyp ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp --depth=. --generator-output=./build -f xcode
|
||||
|
||||
iproj: iproj-cli
|
||||
open ./build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj
|
||||
|
||||
# build Linux project for Xcode (Runs on Mac OS X too, but without platform-specific code)
|
||||
|
||||
@@ -5,14 +5,15 @@ implemented in C++11, currently targeting iOS, OS X, and Ubuntu Linux.
|
||||
|
||||
# Depends
|
||||
|
||||
- Modern C++ compiler that supports `-std=c++11` (On OSX clang++, on Linux g++-4.8 or g++-4.9)
|
||||
- Modern C++ compiler that supports `-std=c++11` (On OS X clang++, on Linux g++-4.8 or g++-4.9)
|
||||
- Boost headers
|
||||
- `libpng`
|
||||
- `libuv`
|
||||
- `glfw3`
|
||||
- `libcurl` (depends on OpenSSL; Linux only)
|
||||
- `libboost_regex` (Linux only)
|
||||
- Homebrew (for build on OS X)
|
||||
- Apple Command Line Tools (for build on OS X; available at [Apple Developer](https://developer.apple.com/downloads))
|
||||
- [Homebrew](http://brew.sh) (for build on OS X)
|
||||
- Python 2.x (for build only)
|
||||
- Node.js (for build only)
|
||||
|
||||
@@ -33,6 +34,8 @@ The demo applications use Mapbox vector tiles, which **require a Mapbox account
|
||||
|
||||
For iOS and OS X use of the demo apps in Xcode, setup the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## OS X
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var mkdirp = require('mkdirp');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var data = JSON.stringify(require(path.join(process.cwd(), process.argv[2])));
|
||||
var out = path.join(process.argv[3], 'style.min.js');
|
||||
|
||||
mkdirp.sync(path.dirname(out));
|
||||
fs.writeFileSync(out, data);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
|
||||
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
|
||||
'GCC_ENABLE_CPP_RTTI':'YES',
|
||||
'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra' ],
|
||||
'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra', '-Wno-variadic-macros' ],
|
||||
'GCC_WARN_PEDANTIC': 'YES',
|
||||
'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE'
|
||||
},
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "glfw_view.hpp"
|
||||
|
||||
#include <mbgl/util/string.hpp>
|
||||
|
||||
GLFWView::GLFWView(bool fullscreen) : fullscreen(fullscreen) {
|
||||
#ifdef NVIDIA
|
||||
glDiscardFramebufferEXT = (PFNGLDISCARDFRAMEBUFFEREXTPROC)glfwGetProcAddress("glDiscardFramebufferEXT");
|
||||
@@ -192,9 +194,15 @@ void GLFWView::make_active() {
|
||||
|
||||
void GLFWView::swap() {
|
||||
glfwPostEmptyEvent();
|
||||
|
||||
double lon, lat, zoom;
|
||||
map->getLonLatZoom(lon, lat, zoom);
|
||||
const double bearing = map->getBearing();
|
||||
const std::string title = mbgl::util::sprintf<128>("Mapbox GL – %.2f/%.6f/%.6f/%.1f", zoom, lat, lon, bearing);
|
||||
glfwSetWindowTitle(window, title.c_str());
|
||||
}
|
||||
|
||||
void GLFWView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) {
|
||||
void GLFWView::notify_map_change(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ void Settings_JSON::load() {
|
||||
longitude = document[rapidjson::SizeType(0)].GetDouble();
|
||||
latitude = document[1].GetDouble();
|
||||
zoom = document[2].GetDouble();
|
||||
angle = document[3].GetDouble();
|
||||
bearing = document[3].GetDouble();
|
||||
debug = document[4].GetBool();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ void Settings_JSON::save() {
|
||||
writer.Double(longitude);
|
||||
writer.Double(latitude);
|
||||
writer.Double(zoom);
|
||||
writer.Double(angle);
|
||||
writer.Double(bearing);
|
||||
writer.Bool(debug);
|
||||
writer.EndArray();
|
||||
}
|
||||
@@ -41,6 +41,6 @@ void Settings_JSON::clear() {
|
||||
longitude = 0;
|
||||
latitude = 0;
|
||||
zoom = 0;
|
||||
angle = 0;
|
||||
bearing = 0;
|
||||
debug = false;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
double longitude = 0;
|
||||
double latitude = 0;
|
||||
double zoom = 0;
|
||||
double angle = 0;
|
||||
double bearing = 0;
|
||||
|
||||
bool debug = false;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
double longitude = 0;
|
||||
double latitude = 0;
|
||||
double zoom = 0;
|
||||
double angle = 0;
|
||||
double bearing = 0;
|
||||
|
||||
bool debug = false;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ Settings_NSUserDefaults::Settings_NSUserDefaults()
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"longitude" : @(longitude),
|
||||
@"latitude" : @(latitude),
|
||||
@"zoom" : @(zoom),
|
||||
@"angle" : @(angle),
|
||||
@"bearing" : @(bearing),
|
||||
@"debug" : @(debug) }];
|
||||
load();
|
||||
}
|
||||
@@ -21,7 +21,7 @@ void Settings_NSUserDefaults::load()
|
||||
longitude = [settings[@"longitude"] doubleValue];
|
||||
latitude = [settings[@"latitude"] doubleValue];
|
||||
zoom = [settings[@"zoom"] doubleValue];
|
||||
angle = [settings[@"angle"] doubleValue];
|
||||
bearing = [settings[@"bearing"] doubleValue];
|
||||
debug = [settings[@"debug"] boolValue];
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ void Settings_NSUserDefaults::save()
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:@{ @"longitude" : @(longitude),
|
||||
@"latitude" : @(latitude),
|
||||
@"zoom" : @(zoom),
|
||||
@"angle" : @(angle),
|
||||
@"zoom" : @(zoom),
|
||||
@"bearing" : @(bearing),
|
||||
@"debug" : @(debug) }];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class IconVertexBuffer : public Buffer<
|
||||
4 + // int16 x/y coordinates per vertex (== 4 bytes)
|
||||
4 // uint16 x/y coordinates of icon in sprite (== 4 bytes)
|
||||
20
|
||||
> {
|
||||
public:
|
||||
typedef int16_t vertex_type;
|
||||
static const double angleFactor;
|
||||
|
||||
size_t add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom);
|
||||
|
||||
void add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace mbgl {
|
||||
|
||||
Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
|
||||
float minScale = 0.0f, int start = 0);
|
||||
float minScale, float maxScale, float tilePixelRatio, int start = 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,17 +26,20 @@ public:
|
||||
// Changes the pixel ratio.
|
||||
bool resize(float newRatio);
|
||||
|
||||
// Update uninitialized sprites in this atlas from the given sprite.
|
||||
// Update uninitialized (= outdated) sprites in this atlas from the given sprite.
|
||||
void update(const Sprite &sprite);
|
||||
|
||||
// Returns the coordinates of a square icon. The getter also *creates* new square icons in the
|
||||
// atlas if they don't exist, but they'll be default-initialized with a a black circle.
|
||||
Rect<dimension> getIcon(int size, const std::string &name);
|
||||
// Returns the coordinates of an image that is sourced from the sprite image.
|
||||
// This getter attempts to read the image from the sprite if it is already loaded.
|
||||
// In that case, it copies it into the sprite atlas and returns the dimensions.
|
||||
// Otherwise, it returns a 0/0/0/0 rect.
|
||||
Rect<dimension> getImage(const std::string &name, const Sprite &sprite);
|
||||
|
||||
// Returns the coordinates of an image that is sourced from the sprite image.
|
||||
// This getter does not create images, as the dimension of the texture us unknown if the
|
||||
// sprite is not yet loaded. Instead, it returns a 0/0/0/0 rect.
|
||||
Rect<dimension> getImage(const std::string &name, const Sprite &sprite);
|
||||
// This getter waits until the sprite image was loaded, copies it into the sprite
|
||||
// image and returns the dimensions.
|
||||
// NEVER CALL THIS FUNCTION FROM THE RENDER THREAD! it is blocking.
|
||||
Rect<dimension> waitForImage(const std::string &name, const Sprite &sprite);
|
||||
|
||||
// Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out
|
||||
// of date.
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
|
||||
#ifndef MBGL_MAP_MAP
|
||||
#define MBGL_MAP_MAP
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/texturepool.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Source;
|
||||
class GlyphAtlas;
|
||||
class GlyphStore;
|
||||
class LayerDescription;
|
||||
class SpriteAtlas;
|
||||
class Sprite;
|
||||
class Style;
|
||||
class StyleLayer;
|
||||
class StyleLayerGroup;
|
||||
class StyleSource;
|
||||
class Texturepool;
|
||||
class FileSource;
|
||||
class View;
|
||||
|
||||
class Map : private util::noncopyable {
|
||||
public:
|
||||
@@ -57,7 +65,8 @@ public:
|
||||
void toggleClass(const std::string &name);
|
||||
const std::vector<std::string> &getAppliedClasses() const;
|
||||
void setDefaultTransitionDuration(uint64_t duration_milliseconds = 0);
|
||||
void setStyleJSON(std::string newStyleJSON);
|
||||
void setStyleURL(const std::string &url);
|
||||
void setStyleJSON(std::string newStyleJSON, const std::string &base = "");
|
||||
std::string getStyleJSON() const;
|
||||
void setAccessToken(std::string access_token);
|
||||
std::string getAccessToken() const;
|
||||
@@ -89,9 +98,9 @@ public:
|
||||
|
||||
// Rotation
|
||||
void rotateBy(double sx, double sy, double ex, double ey, double duration = 0);
|
||||
void setAngle(double angle, double duration = 0);
|
||||
void setAngle(double angle, double cx, double cy);
|
||||
double getAngle() const;
|
||||
void setBearing(double degrees, double duration = 0);
|
||||
void setBearing(double degrees, double cx, double cy);
|
||||
double getBearing() const;
|
||||
void resetNorth();
|
||||
void startRotating();
|
||||
void stopRotating();
|
||||
@@ -104,10 +113,12 @@ public:
|
||||
|
||||
public:
|
||||
inline const TransformState &getState() const { return state; }
|
||||
inline std::shared_ptr<const Style> getStyle() const { return style; }
|
||||
inline std::shared_ptr<FileSource> getFileSource() const { return fileSource; }
|
||||
inline std::shared_ptr<Style> getStyle() const { return style; }
|
||||
inline std::shared_ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; }
|
||||
inline std::shared_ptr<GlyphStore> getGlyphStore() { return glyphStore; }
|
||||
inline std::shared_ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
|
||||
std::shared_ptr<Sprite> getSprite();
|
||||
inline std::shared_ptr<Texturepool> getTexturepool() { return texturepool; }
|
||||
inline std::shared_ptr<uv::loop> getLoop() { return loop; }
|
||||
inline timestamp getAnimationTime() const { return animationTime; }
|
||||
@@ -142,6 +153,14 @@ private:
|
||||
void renderLayers(std::shared_ptr<StyleLayerGroup> group);
|
||||
void renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass);
|
||||
|
||||
private:
|
||||
bool async = false;
|
||||
std::shared_ptr<uv::loop> loop;
|
||||
uv_thread_t thread;
|
||||
uv_async_t *async_terminate = nullptr;
|
||||
uv_async_t *async_render = nullptr;
|
||||
uv_async_t *async_cleanup = nullptr;
|
||||
|
||||
private:
|
||||
// If cleared, the next time the render thread attempts to render the map, it will *actually*
|
||||
// render the map.
|
||||
@@ -162,10 +181,13 @@ private:
|
||||
Transform transform;
|
||||
TransformState state;
|
||||
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
|
||||
std::shared_ptr<Style> style;
|
||||
std::shared_ptr<GlyphAtlas> glyphAtlas;
|
||||
std::shared_ptr<GlyphStore> glyphStore;
|
||||
std::shared_ptr<SpriteAtlas> spriteAtlas;
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
std::shared_ptr<Texturepool> texturepool;
|
||||
|
||||
Painter painter;
|
||||
@@ -180,13 +202,6 @@ private:
|
||||
|
||||
std::set<std::shared_ptr<StyleSource>> activeSources;
|
||||
|
||||
private:
|
||||
bool async = false;
|
||||
std::shared_ptr<uv::loop> loop;
|
||||
uv_thread_t thread;
|
||||
uv_async_t *async_terminate = nullptr;
|
||||
uv_async_t *async_render = nullptr;
|
||||
uv_async_t *async_cleanup = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
#ifndef MBGL_MAP_RASTER_TILE_DATA
|
||||
#define MBGL_MAP_RASTER_TILE_DATA
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
|
||||
#include <mbgl/renderer/raster_bucket.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
class Painter;
|
||||
class SourceInfo;
|
||||
class StyleLayer;
|
||||
|
||||
class RasterTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
RasterTileData(Tile::ID id, Map &map, const std::string url);
|
||||
RasterTileData(Tile::ID id, Map &map, const SourceInfo &source);
|
||||
~RasterTileData();
|
||||
|
||||
virtual void parse();
|
||||
|
||||
@@ -3,30 +3,29 @@
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <cstdint>
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
class Painter;
|
||||
class StyleLayer;
|
||||
class TransformState;
|
||||
class Texturepool;
|
||||
struct box;
|
||||
|
||||
class Source : public std::enable_shared_from_this<Source>, private util::noncopyable {
|
||||
public:
|
||||
Source(StyleSource style_source, const std::string &access_token = "");
|
||||
Source(SourceType type = SourceType::Vector, const std::string &url = "",
|
||||
uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 22,
|
||||
const std::string &access_token = "");
|
||||
Source(SourceInfo info, const std::string &access_token = "");
|
||||
|
||||
bool update(Map &map);
|
||||
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
|
||||
@@ -41,11 +40,7 @@ public:
|
||||
static std::string normalizeSourceURL(const std::string &url, const std::string &access_token);
|
||||
|
||||
public:
|
||||
const SourceType type;
|
||||
const std::string url;
|
||||
const uint32_t tile_size;
|
||||
const int32_t min_zoom;
|
||||
const int32_t max_zoom;
|
||||
const SourceInfo info;
|
||||
|
||||
private:
|
||||
bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#ifndef MBGL_STYLE_SPRITE
|
||||
#define MBGL_STYLE_SPRITE
|
||||
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <future>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
class FileSource;
|
||||
|
||||
class SpritePosition {
|
||||
public:
|
||||
@@ -28,36 +30,47 @@ public:
|
||||
uint8_t pixelRatio = 1;
|
||||
};
|
||||
|
||||
class Sprite : public std::enable_shared_from_this<Sprite> {
|
||||
public:
|
||||
Sprite(Map &map, float pixelRatio = 1);
|
||||
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
|
||||
private:
|
||||
struct Key {};
|
||||
void load(const std::shared_ptr<FileSource> &fileSource);
|
||||
|
||||
void load(const std::string& base_url);
|
||||
public:
|
||||
Sprite(const Key &, const std::string& base_url, float pixelRatio);
|
||||
static std::shared_ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource);
|
||||
|
||||
const SpritePosition &getSpritePosition(const std::string& name) const;
|
||||
|
||||
void waitUntilLoaded() const;
|
||||
bool isLoaded() const;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
private:
|
||||
const bool valid;
|
||||
|
||||
public:
|
||||
const float pixelRatio;
|
||||
const std::string spriteURL;
|
||||
const std::string jsonURL;
|
||||
std::unique_ptr<util::Image> raster;
|
||||
|
||||
private:
|
||||
void asyncParseJSON();
|
||||
void asyncParseImage();
|
||||
|
||||
static void parseJSON(std::shared_ptr<Sprite> &sprite);
|
||||
static void parseImage(std::shared_ptr<Sprite> &sprite);
|
||||
static void complete(std::shared_ptr<Sprite> &sprite);
|
||||
void parseJSON();
|
||||
void parseImage();
|
||||
void complete();
|
||||
|
||||
private:
|
||||
Map ↦
|
||||
std::string url;
|
||||
std::string body;
|
||||
std::string image;
|
||||
std::atomic<bool> loaded;
|
||||
std::atomic<bool> loadedImage;
|
||||
std::atomic<bool> loadedJSON;
|
||||
std::unordered_map<std::string, SpritePosition> pos;
|
||||
const SpritePosition empty;
|
||||
|
||||
std::promise<void> promise;
|
||||
std::future<void> future;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#ifndef MBGL_MAP_TILE
|
||||
#define MBGL_MAP_TILE
|
||||
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <forward_list>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <forward_list>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
#define MBGL_MAP_TILE_DATA
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/geometry/vao.hpp>
|
||||
#include <mbgl/renderer/debug_bucket.hpp>
|
||||
#include <mbgl/geometry/debug_font_buffer.hpp>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
class Painter;
|
||||
class SourceInfo;
|
||||
class StyleLayer;
|
||||
|
||||
namespace platform { class Request; }
|
||||
|
||||
class TileData : public std::enable_shared_from_this<TileData>,
|
||||
private util::noncopyable {
|
||||
public:
|
||||
@@ -37,7 +41,7 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
TileData(Tile::ID id, Map &map, const std::string url);
|
||||
TileData(Tile::ID id, Map &map, const SourceInfo &source);
|
||||
~TileData();
|
||||
|
||||
void request();
|
||||
@@ -60,8 +64,13 @@ public:
|
||||
protected:
|
||||
Map ↦
|
||||
|
||||
public:
|
||||
const SourceInfo &source;
|
||||
|
||||
// Request-related information.
|
||||
const std::string url;
|
||||
|
||||
protected:
|
||||
std::weak_ptr<platform::Request> req;
|
||||
std::string data;
|
||||
|
||||
|
||||
@@ -2,27 +2,30 @@
|
||||
#define MBGL_MAP_TILE_PARSER
|
||||
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/utf.hpp>
|
||||
#include <mbgl/style/filter_expression.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/text/collision.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Style;
|
||||
class Bucket;
|
||||
class FontStack;
|
||||
class GlyphAtlas;
|
||||
class GlyphStore;
|
||||
class SpriteAtlas;
|
||||
class VectorTileData;
|
||||
class StyleLayer;
|
||||
class StyleLayerGroup;
|
||||
class Sprite;
|
||||
class Style;
|
||||
class StyleBucket;
|
||||
class StyleBucketFill;
|
||||
class StyleBucketLine;
|
||||
class StyleBucketIcon;
|
||||
|
||||
class Bucket;
|
||||
class StyleBucketSymbol;
|
||||
class StyleLayerGroup;
|
||||
class VectorTileData;
|
||||
|
||||
class TileParser {
|
||||
public:
|
||||
@@ -30,25 +33,22 @@ public:
|
||||
const std::shared_ptr<const Style> &style,
|
||||
const std::shared_ptr<GlyphAtlas> &glyphAtlas,
|
||||
const std::shared_ptr<GlyphStore> &glyphStore,
|
||||
const std::shared_ptr<SpriteAtlas> &spriteAtlas);
|
||||
const std::shared_ptr<SpriteAtlas> &spriteAtlas,
|
||||
const std::shared_ptr<Sprite> &sprite);
|
||||
|
||||
public:
|
||||
void parse();
|
||||
|
||||
private:
|
||||
bool obsolete() const;
|
||||
void parseGlyphs();
|
||||
void parseStyleLayers(std::shared_ptr<StyleLayerGroup> group);
|
||||
void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
|
||||
std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc);
|
||||
|
||||
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
|
||||
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line);
|
||||
std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon);
|
||||
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text);
|
||||
std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol);
|
||||
|
||||
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter);
|
||||
template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args);
|
||||
template <class Bucket> void addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter);
|
||||
|
||||
private:
|
||||
const VectorTile vector_data;
|
||||
@@ -59,8 +59,9 @@ private:
|
||||
std::shared_ptr<GlyphAtlas> glyphAtlas;
|
||||
std::shared_ptr<GlyphStore> glyphStore;
|
||||
std::shared_ptr<SpriteAtlas> spriteAtlas;
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
|
||||
Placement placement;
|
||||
Collision collision;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
#ifndef MBGL_MAP_TRANSFORM
|
||||
#define MBGL_MAP_TRANSFORM
|
||||
|
||||
#include <mbgl/util/transition.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
|
||||
#include "view.hpp"
|
||||
#include "transform_state.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct box;
|
||||
class View;
|
||||
namespace util { class transition; }
|
||||
|
||||
class Transform : private util::noncopyable {
|
||||
class Transform : private util::noncopyable {
|
||||
public:
|
||||
Transform(View &view);
|
||||
|
||||
@@ -93,7 +96,7 @@ private:
|
||||
const double max_scale = std::pow(2, 18);
|
||||
|
||||
// cache values for spherical mercator math
|
||||
double zc, Bc, Cc;
|
||||
double Bc, Cc;
|
||||
|
||||
std::forward_list<std::shared_ptr<util::transition>> transitions;
|
||||
std::shared_ptr<util::transition> scale_timeout;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#ifndef MBGL_MAP_TRANSFORM_STATE
|
||||
#define MBGL_MAP_TRANSFORM_STATE
|
||||
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/map/tile.hpp>
|
||||
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Transform;
|
||||
|
||||
class TransformState {
|
||||
friend class Transform;
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
#ifndef MBGL_MAP_VECTOR_TILE
|
||||
#define MBGL_MAP_VECTOR_TILE
|
||||
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/style/filter_expression.hpp>
|
||||
#include <mbgl/style/value.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/style/filter_expression.hpp>
|
||||
#include <vector>
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class BucketDescription;
|
||||
class VectorTileLayer;
|
||||
|
||||
struct pbf;
|
||||
|
||||
enum class FeatureType {
|
||||
Unknown = 0,
|
||||
Point = 1,
|
||||
@@ -88,7 +86,7 @@ private:
|
||||
const FilterExpression& filterExpression;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const GlyphPlacement& placement);
|
||||
std::ostream& operator<<(std::ostream&, const PositionedGlyph& placement);
|
||||
|
||||
class VectorTileLayer {
|
||||
public:
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
#ifndef MBGL_MAP_VECTOR_TILE_DATA
|
||||
#define MBGL_MAP_VECTOR_TILE_DATA
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
|
||||
|
||||
#include <mbgl/renderer/bucket.hpp>
|
||||
|
||||
#include <mbgl/geometry/vertex_buffer.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/fill_buffer.hpp>
|
||||
#include <mbgl/geometry/line_buffer.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/geometry/line_buffer.hpp>
|
||||
#include <mbgl/geometry/text_buffer.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Bucket;
|
||||
class Map;
|
||||
class Painter;
|
||||
class SourceInfo;
|
||||
class StyleLayer;
|
||||
class TileParser;
|
||||
|
||||
class VectorTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
VectorTileData(Tile::ID id, Map &map, const std::string url);
|
||||
VectorTileData(Tile::ID id, Map &map, const SourceInfo &source);
|
||||
~VectorTileData();
|
||||
|
||||
virtual void beforeParse();
|
||||
@@ -48,6 +51,9 @@ protected:
|
||||
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
|
||||
|
||||
std::unique_ptr<TileParser> parser;
|
||||
|
||||
public:
|
||||
const float depth;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ constexpr EventSeverity disabledEventSeverities[] = {
|
||||
|
||||
|
||||
constexpr Event disabledEvents[] = {
|
||||
Event(-1) // Avoid zero size array
|
||||
};
|
||||
|
||||
constexpr EventPermutation disabledEventPermutations[] = {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef MBGL_RENDERER_ICONBUCKET
|
||||
#define MBGL_RENDERER_ICONBUCKET
|
||||
|
||||
#include <mbgl/renderer/bucket.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#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<StyleLayer> 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
|
||||
@@ -38,8 +38,7 @@ class StyleSource;
|
||||
|
||||
class FillBucket;
|
||||
class LineBucket;
|
||||
class IconBucket;
|
||||
class TextBucket;
|
||||
class SymbolBucket;
|
||||
class RasterBucket;
|
||||
|
||||
struct FillProperties;
|
||||
@@ -62,6 +61,8 @@ public:
|
||||
void cleanup();
|
||||
|
||||
|
||||
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
|
||||
// tiles whatsoever.
|
||||
void clear();
|
||||
|
||||
// Updates the default matrices to the current viewport dimensions.
|
||||
@@ -73,10 +74,6 @@ public:
|
||||
// Renders debug information for a tile.
|
||||
void renderTileDebug(const Tile& tile);
|
||||
|
||||
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
|
||||
// tiles whatsoever.
|
||||
void renderMatte();
|
||||
|
||||
// Renders the red debug frame around a tile, visualizing its perimeter.
|
||||
void renderDebugFrame();
|
||||
|
||||
@@ -85,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<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
void renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
void renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
void renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
void renderSymbol(SymbolBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
void renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
|
||||
|
||||
void preparePrerender(PrerenderedTexture &texture);
|
||||
@@ -121,7 +117,7 @@ public:
|
||||
bool needsAnimation() const;
|
||||
private:
|
||||
void setupShaders();
|
||||
const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Default);
|
||||
const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Map);
|
||||
|
||||
void prepareTile(const Tile& tile);
|
||||
|
||||
|
||||
109
include/mbgl/renderer/symbol_bucket.hpp
Normal file
109
include/mbgl/renderer/symbol_bucket.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#ifndef MBGL_RENDERER_SYMBOLBUCKET
|
||||
#define MBGL_RENDERER_SYMBOLBUCKET
|
||||
|
||||
#include "bucket.hpp"
|
||||
#include <mbgl/geometry/vao.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/text_buffer.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/text/types.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Style;
|
||||
class TextShader;
|
||||
class IconShader;
|
||||
class DotShader;
|
||||
class Collision;
|
||||
class SpriteAtlas;
|
||||
class Sprite;
|
||||
class GlyphAtlas;
|
||||
class GlyphStore;
|
||||
class FontStack;
|
||||
|
||||
class SymbolFeature {
|
||||
public:
|
||||
pbf geometry;
|
||||
std::u32string label;
|
||||
std::string sprite;
|
||||
};
|
||||
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
vec2<float> tl, tr, bl, br;
|
||||
Rect<uint16_t> tex;
|
||||
float angle;
|
||||
float minScale = 0.0f;
|
||||
float maxScale = std::numeric_limits<float>::infinity();
|
||||
CollisionAnchor anchor;
|
||||
};
|
||||
|
||||
typedef std::vector<Symbol> Symbols;
|
||||
|
||||
|
||||
class SymbolBucket : public Bucket {
|
||||
public:
|
||||
SymbolBucket(const StyleBucketSymbol &properties, Collision &collision);
|
||||
|
||||
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> 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, Sprite &sprite,
|
||||
GlyphAtlas &glyphAtlas, GlyphStore &glyphStore);
|
||||
|
||||
void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange,
|
||||
float zoom);
|
||||
|
||||
void drawGlyphs(TextShader &shader);
|
||||
void drawIcons(IconShader& shader);
|
||||
|
||||
private:
|
||||
|
||||
std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite);
|
||||
|
||||
|
||||
void addFeature(const pbf &geom_pbf, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image);
|
||||
void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image);
|
||||
|
||||
|
||||
// Adds placed items to the buffer.
|
||||
template <typename Buffer>
|
||||
void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange);
|
||||
|
||||
// Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer.
|
||||
void addGlyphsToAtlas(uint64_t tileid, const std::string stackname, const std::u32string &string,
|
||||
const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
|
||||
|
||||
public:
|
||||
const StyleBucketSymbol &properties;
|
||||
|
||||
private:
|
||||
Collision &collision;
|
||||
|
||||
struct {
|
||||
TextVertexBuffer vertices;
|
||||
TriangleElementsBuffer triangles;
|
||||
std::vector<ElementGroup> groups;
|
||||
} text;
|
||||
|
||||
struct {
|
||||
IconVertexBuffer vertices;
|
||||
TriangleElementsBuffer triangles;
|
||||
std::vector<ElementGroup> groups;
|
||||
} icon;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef MBGL_RENDERER_TEXTBUCKET
|
||||
#define MBGL_RENDERER_TEXTBUCKET
|
||||
|
||||
#include "bucket.hpp"
|
||||
#include <mbgl/geometry/vao.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/text/types.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
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<StyleLayer> 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<triangle_group_type> triangleGroups;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,30 +11,57 @@ public:
|
||||
|
||||
void bind(char *offset);
|
||||
|
||||
void setImage(int32_t image);
|
||||
void setColor(const std::array<float, 4>& color);
|
||||
void setDimension(const std::array<float, 2>& dimension);
|
||||
void setSize(float size);
|
||||
void setRatio(float ratio);
|
||||
void setExtrudeMatrix(const std::array<float, 16>& exmatrix);
|
||||
void setAngle(float angle);
|
||||
void setZoom(float zoom);
|
||||
void setFlip(float flip);
|
||||
void setFadeDist(float fadedist);
|
||||
void setMinFadeZoom(float minfadezoom);
|
||||
void setMaxFadeZoom(float maxfadezoom);
|
||||
void setFadeZoom(float fadezoom);
|
||||
void setOpacity(float opacity);
|
||||
void setTextureSize(const std::array<float, 2> &texsize);
|
||||
|
||||
private:
|
||||
int32_t a_pos = -1;
|
||||
int32_t a_offset = -1;
|
||||
int32_t a_tex = -1;
|
||||
int32_t a_angle = -1;
|
||||
int32_t a_minzoom = -1;
|
||||
int32_t a_maxzoom = -1;
|
||||
int32_t a_rangeend = -1;
|
||||
int32_t a_rangestart = -1;
|
||||
int32_t a_labelminzoom = -1;
|
||||
|
||||
int32_t image = -1;
|
||||
int32_t u_image = -1;
|
||||
std::array<float, 16> exmatrix = {{}};
|
||||
int32_t u_exmatrix = -1;
|
||||
|
||||
std::array<float, 4> color = {{}};
|
||||
int32_t u_color = -1;
|
||||
float angle = 0;
|
||||
int32_t u_angle = -1;
|
||||
|
||||
std::array<float, 2> dimension = {{}};
|
||||
int32_t u_dimension = -1;
|
||||
float zoom = 0;
|
||||
int32_t u_zoom = -1;
|
||||
|
||||
float size = 0;
|
||||
int32_t u_size = -1;
|
||||
float flip = 0;
|
||||
int32_t u_flip = -1;
|
||||
|
||||
float ratio = 0;
|
||||
int32_t u_ratio = -1;
|
||||
float fadedist = 0;
|
||||
int32_t u_fadedist = -1;
|
||||
|
||||
float minfadezoom = 0;
|
||||
int32_t u_minfadezoom = -1;
|
||||
|
||||
float maxfadezoom = 0;
|
||||
int32_t u_maxfadezoom = -1;
|
||||
|
||||
float fadezoom = 0;
|
||||
int32_t u_fadezoom = -1;
|
||||
|
||||
float opacity = 0;
|
||||
int32_t u_opacity = -1;
|
||||
|
||||
std::array<float, 2> texsize = {{}};
|
||||
int32_t u_texsize = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
void setLineWidth(const std::array<float, 2>& linewidth);
|
||||
void setRatio(float ratio);
|
||||
void setDashArray(const std::array<float, 2>& dasharray);
|
||||
void setBlur(float blur);
|
||||
void setDebug(float debug);
|
||||
|
||||
private:
|
||||
@@ -37,6 +38,9 @@ private:
|
||||
|
||||
std::array<float, 2> dasharray = {{}};
|
||||
int32_t u_dasharray = -1;
|
||||
|
||||
float blur = 0.0f;
|
||||
int32_t u_blur = -1;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,15 @@ enum class PropertyKey {
|
||||
|
||||
IconOpacity,
|
||||
IconRotate,
|
||||
IconRotateAnchor,
|
||||
IconSize,
|
||||
IconColor,
|
||||
IconHaloColor,
|
||||
IconHaloWidth,
|
||||
IconHaloBlur,
|
||||
IconTranslate, // for transitions only
|
||||
IconTranslateX,
|
||||
IconTranslateY,
|
||||
IconTranslateAnchor,
|
||||
|
||||
TextOpacity,
|
||||
TextSize,
|
||||
@@ -38,6 +46,10 @@ enum class PropertyKey {
|
||||
TextHaloColor,
|
||||
TextHaloWidth,
|
||||
TextHaloBlur,
|
||||
TextTranslate, // for transitions only
|
||||
TextTranslateX,
|
||||
TextTranslateY,
|
||||
TextTranslateAnchor,
|
||||
|
||||
CompositeOpacity,
|
||||
|
||||
|
||||
@@ -48,13 +48,15 @@ public:
|
||||
|
||||
const BackgroundProperties &getBackgroundProperties() const;
|
||||
|
||||
const std::string &getSpriteURL() const;
|
||||
|
||||
public:
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
std::shared_ptr<StyleLayerGroup> layers;
|
||||
std::vector<std::string> appliedClasses;
|
||||
std::string sprite_url;
|
||||
std::string glyph_url;
|
||||
|
||||
private:
|
||||
std::string sprite_url;
|
||||
|
||||
private:
|
||||
PropertyTransition defaultTransition;
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/filter_expression.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <forward_list>
|
||||
@@ -16,56 +18,74 @@ 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<float> translate {0, 0};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Default;
|
||||
std::string icon;
|
||||
float spacing = 0.0f;
|
||||
float padding = 2.0f;
|
||||
};
|
||||
// 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;
|
||||
|
||||
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<float> translate {0, 0};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Default;
|
||||
float max_angle_delta = M_PI;
|
||||
PlacementType placement = PlacementType::Point;
|
||||
float min_distance = 250.0f;
|
||||
float rotate = 0.0f; // what is this?
|
||||
float padding = 2.0f;
|
||||
float slant = 0.0f;
|
||||
bool always_visible = false;
|
||||
|
||||
struct {
|
||||
bool allow_overlap = false;
|
||||
bool ignore_placement = false;
|
||||
bool optional = false;
|
||||
RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
|
||||
float max_size = 1.0f;
|
||||
std::string image;
|
||||
float rotate = 0.0f;
|
||||
float padding = 2.0f;
|
||||
bool keep_upright = false;
|
||||
vec2<float> offset = {0, 0};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
|
||||
} icon;
|
||||
|
||||
struct {
|
||||
RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
|
||||
std::string field;
|
||||
std::string font;
|
||||
float max_size = 16.0f;
|
||||
float max_width = 15.0f * 24 /* em */;
|
||||
float line_height = 1.2f * 24 /* em */;
|
||||
float letter_spacing = 0.0f * 24 /* em */;
|
||||
TextJustifyType justify = TextJustifyType::Center;
|
||||
TextHorizontalAlignType horizontal_align = TextHorizontalAlignType::Center;
|
||||
TextVerticalAlignType vertical_align = TextVerticalAlignType::Center;
|
||||
float max_angle = 45.0f /* degrees */;
|
||||
float rotate = 0.0f;
|
||||
float slant = 0.0f;
|
||||
float padding = 2.0f;
|
||||
bool keep_upright = true;
|
||||
TextTransformType transform = TextTransformType::None;
|
||||
vec2<float> offset = {0, 0};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
|
||||
bool allow_overlap = false;
|
||||
bool ignore_placement = false;
|
||||
bool optional = false;
|
||||
} text;
|
||||
};
|
||||
|
||||
class StyleBucketRaster {
|
||||
public:
|
||||
};
|
||||
|
||||
typedef util::variant<StyleBucketFill, StyleBucketLine, StyleBucketIcon,
|
||||
StyleBucketText, StyleBucketRaster,
|
||||
std::false_type> StyleBucketRender;
|
||||
typedef util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbol,
|
||||
StyleBucketRaster, std::false_type> StyleBucketRender;
|
||||
|
||||
|
||||
class StyleBucket {
|
||||
@@ -79,6 +99,8 @@ public:
|
||||
std::string source_layer;
|
||||
FilterExpression filter;
|
||||
StyleBucketRender render = std::false_type();
|
||||
float min_zoom = -std::numeric_limits<float>::infinity();
|
||||
float max_zoom = std::numeric_limits<float>::infinity();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ private:
|
||||
// Parses optional properties into a render bucket.
|
||||
template<typename T>
|
||||
bool parseRenderProperty(JSVal value, T &target, const char *name);
|
||||
template <typename T, typename Parser>
|
||||
bool parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser);
|
||||
template <typename Parser, typename T>
|
||||
bool parseRenderProperty(JSVal value, T &target, const char *name);
|
||||
|
||||
// Parses optional properties into style class properties.
|
||||
template <typename T>
|
||||
|
||||
@@ -19,7 +19,7 @@ struct FillProperties {
|
||||
Color fill_color = {{ 0, 0, 0, 1 }};
|
||||
Color stroke_color = {{ 0, 0, 0, -1 }};
|
||||
std::array<float, 2> translate = {{ 0, 0 }};
|
||||
TranslateAnchorType translateAnchor = TranslateAnchorType::Default;
|
||||
TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
|
||||
std::string image;
|
||||
|
||||
inline bool isVisible() const {
|
||||
@@ -32,8 +32,8 @@ struct LineProperties {
|
||||
float opacity = 1.0f;
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
std::array<float, 2> translate = {{ 0, 0 }};
|
||||
TranslateAnchorType translateAnchor = TranslateAnchorType::Default;
|
||||
float width = 0;
|
||||
TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
|
||||
float width = 1;
|
||||
float offset = 0;
|
||||
float blur = 0;
|
||||
std::array<float, 2> dash_array = {{ 1, -1 }};
|
||||
@@ -44,30 +44,38 @@ struct LineProperties {
|
||||
}
|
||||
};
|
||||
|
||||
struct IconProperties {
|
||||
inline IconProperties() {}
|
||||
float opacity = 1.0f;
|
||||
float rotate = 0.0f;
|
||||
RotateAnchorType rotate_anchor = RotateAnchorType::Default;
|
||||
struct SymbolProperties {
|
||||
inline SymbolProperties() {}
|
||||
|
||||
struct {
|
||||
float opacity = 1.0f;
|
||||
float rotate = 0.0f;
|
||||
float size = 1.0f;
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
Color halo_color = {{ 0, 0, 0, 0 }};
|
||||
float halo_width = 0.0f;
|
||||
float halo_blur = 0.0f;
|
||||
std::array<float, 2> translate = {{ 0, 0 }};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
|
||||
} icon;
|
||||
|
||||
struct {
|
||||
float opacity = 1.0f;
|
||||
float size = 12.0f;
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
Color halo_color = {{ 0, 0, 0, 0 }};
|
||||
float halo_width = 0.0f;
|
||||
float halo_blur = 0.0f;
|
||||
std::array<float, 2> translate = {{ 0, 0 }};
|
||||
TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
|
||||
} text;
|
||||
|
||||
inline bool isVisible() const {
|
||||
return opacity > 0;
|
||||
return (icon.opacity > 0 && (icon.color[3] > 0 || icon.halo_color[3] > 0) && icon.size > 0) ||
|
||||
(text.opacity > 0 && (text.color[3] > 0 || text.halo_color[3] > 0) && text.size > 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 CompositeProperties {
|
||||
inline CompositeProperties() {}
|
||||
@@ -94,14 +102,13 @@ struct RasterProperties {
|
||||
|
||||
struct BackgroundProperties {
|
||||
inline BackgroundProperties() {}
|
||||
Color color = {{ 1, 1, 1, 1 }};
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
};
|
||||
|
||||
typedef util::variant<
|
||||
FillProperties,
|
||||
LineProperties,
|
||||
IconProperties,
|
||||
TextProperties,
|
||||
SymbolProperties,
|
||||
CompositeProperties,
|
||||
RasterProperties,
|
||||
BackgroundProperties,
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace mbgl {
|
||||
|
||||
class Source;
|
||||
|
||||
class StyleSource {
|
||||
class SourceInfo {
|
||||
public:
|
||||
const SourceType type;
|
||||
const std::string url;
|
||||
@@ -17,13 +17,21 @@ public:
|
||||
const int32_t min_zoom;
|
||||
const int32_t max_zoom;
|
||||
|
||||
SourceInfo(SourceType type = SourceType::Vector, const std::string &url = "",
|
||||
uint32_t tile_size = 512, int32_t min_zoom = 0, int32_t max_zoom = 22)
|
||||
: type(type), url(url), tile_size(tile_size), min_zoom(min_zoom), max_zoom(max_zoom) {}
|
||||
};
|
||||
|
||||
|
||||
class StyleSource {
|
||||
public:
|
||||
const SourceInfo info;
|
||||
|
||||
bool enabled = false;
|
||||
std::shared_ptr<Source> source;
|
||||
|
||||
StyleSource(SourceType type = SourceType::Vector, const std::string &url = "",
|
||||
uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 22)
|
||||
: type(type), url(url), tile_size(tile_size), min_zoom(min_zoom), max_zoom(max_zoom) {}
|
||||
};
|
||||
StyleSource(const SourceInfo &info) : info(info) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,12 +11,13 @@ namespace mbgl {
|
||||
// Stores a premultiplied color, with all four channels ranging from 0..1
|
||||
typedef std::array<float, 4> Color;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class StyleLayerType : uint8_t {
|
||||
Unknown,
|
||||
Fill,
|
||||
Line,
|
||||
Icon,
|
||||
Text,
|
||||
Symbol,
|
||||
Raster,
|
||||
Composite,
|
||||
Background
|
||||
@@ -26,133 +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 {
|
||||
EvenOdd,
|
||||
NonZero,
|
||||
Default = NonZero
|
||||
};
|
||||
|
||||
enum class CapType : uint8_t {
|
||||
None,
|
||||
Round,
|
||||
Butt,
|
||||
Square,
|
||||
Default = None
|
||||
};
|
||||
|
||||
enum class JoinType : uint8_t {
|
||||
None,
|
||||
Miter,
|
||||
Bevel,
|
||||
Round,
|
||||
Default = None
|
||||
};
|
||||
|
||||
enum class TextPathType : uint8_t {
|
||||
Horizontal,
|
||||
Curve,
|
||||
Default = Horizontal
|
||||
};
|
||||
|
||||
enum class TextTransformType : uint8_t {
|
||||
None,
|
||||
Uppercase,
|
||||
Lowercase,
|
||||
Default = None
|
||||
};
|
||||
|
||||
enum class TranslateAnchorType : uint8_t {
|
||||
Map,
|
||||
Viewport,
|
||||
Default = Map
|
||||
};
|
||||
|
||||
enum class RotateAnchorType : uint8_t {
|
||||
Map,
|
||||
Viewport,
|
||||
Default = Viewport
|
||||
};
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class SourceType : uint8_t {
|
||||
Vector,
|
||||
Raster,
|
||||
GeoJSON,
|
||||
Video,
|
||||
Default = Vector
|
||||
Video
|
||||
};
|
||||
|
||||
inline WindingType parseWindingType(const std::string &type) {
|
||||
if (type == "even-odd") return WindingType::EvenOdd;
|
||||
if (type == "non-zero") return WindingType::NonZero;
|
||||
return WindingType::Default;
|
||||
}
|
||||
MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
|
||||
{ SourceType::Vector, "vector" },
|
||||
{ SourceType::Raster, "raster" },
|
||||
{ SourceType::GeoJSON, "geojson" },
|
||||
{ SourceType::Video, "video" },
|
||||
});
|
||||
|
||||
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::None;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
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::None;
|
||||
}
|
||||
|
||||
inline TextPathType parseTextPathType(const std::string &path) {
|
||||
if (path == "horizontal") return TextPathType::Horizontal;
|
||||
if (path == "curve") return TextPathType::Curve;
|
||||
return TextPathType::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 WindingType : bool {
|
||||
EvenOdd,
|
||||
NonZero,
|
||||
};
|
||||
|
||||
inline TranslateAnchorType parseTranslateAnchorType(const std::string &anchor) {
|
||||
if (anchor == "map") return TranslateAnchorType::Map;
|
||||
if (anchor == "viewport") return TranslateAnchorType::Viewport;
|
||||
return TranslateAnchorType::Default;
|
||||
}
|
||||
MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, {
|
||||
{ WindingType::EvenOdd, "even-odd" },
|
||||
{ WindingType::NonZero, "non-zero" },
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
enum class CapType : uint8_t {
|
||||
Round,
|
||||
Butt,
|
||||
Square,
|
||||
};
|
||||
|
||||
inline float parseVerticalAlignmentType(const std::string &alignment) {
|
||||
if (alignment == "bottom") return 1.0f;
|
||||
if (alignment == "top") return 0.0f;
|
||||
return 0.5f;
|
||||
}
|
||||
MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, {
|
||||
{ CapType::Round, "round" },
|
||||
{ CapType::Butt, "butt" },
|
||||
{ CapType::Square, "square" },
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class JoinType : uint8_t {
|
||||
Miter,
|
||||
Bevel,
|
||||
Round
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
|
||||
{ JoinType::Miter, "miter" },
|
||||
{ JoinType::Bevel, "bevel" },
|
||||
{ JoinType::Round, "round" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class TranslateAnchorType : bool {
|
||||
Map,
|
||||
Viewport
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, {
|
||||
{ TranslateAnchorType::Map, "map" },
|
||||
{ TranslateAnchorType::Viewport, "viewport" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class RotateAnchorType : bool {
|
||||
Map,
|
||||
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,
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, {
|
||||
{ RotationAlignmentType::Map, "map" },
|
||||
{ RotationAlignmentType::Viewport, "viewport" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class TextJustifyType : uint8_t {
|
||||
Center,
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, {
|
||||
{ TextJustifyType::Center, "center" },
|
||||
{ TextJustifyType::Left, "left" },
|
||||
{ TextJustifyType::Right, "right" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class TextHorizontalAlignType : uint8_t {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(TextHorizontalAlignTypeClass, TextHorizontalAlignType, {
|
||||
{ TextHorizontalAlignType::Left, "left" },
|
||||
{ TextHorizontalAlignType::Center, "center" },
|
||||
{ TextHorizontalAlignType::Right, "right" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class TextVerticalAlignType : uint8_t {
|
||||
Top,
|
||||
Center,
|
||||
Bottom,
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(TextVerticalAlignTypeClass, TextVerticalAlignType, {
|
||||
{ TextVerticalAlignType::Top, "top" },
|
||||
{ TextVerticalAlignType::Center, "center" },
|
||||
{ TextVerticalAlignType::Bottom, "bottom" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class TextTransformType : uint8_t {
|
||||
None,
|
||||
Uppercase,
|
||||
Lowercase,
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, {
|
||||
{ TextTransformType::None, "none" },
|
||||
{ TextTransformType::Uppercase, "uppercase" },
|
||||
{ TextTransformType::Lowercase, "lowercase" },
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,24 +8,23 @@ namespace mbgl {
|
||||
class Collision {
|
||||
|
||||
public:
|
||||
Collision();
|
||||
Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1);
|
||||
~Collision();
|
||||
|
||||
PlacementProperty place(const GlyphBoxes &boxes,
|
||||
const CollisionAnchor &anchor,
|
||||
float minPlacementScale, float maxPlacementScale,
|
||||
float padding, bool horizontal, bool alwaysVisible);
|
||||
float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale,
|
||||
float maxPlacementScale, float pad);
|
||||
PlacementRange getPlacementRange(const GlyphBoxes &glyphs,
|
||||
float placementScale, bool horizontal);
|
||||
void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
|
||||
float placementScale, const PlacementRange &placementRange,
|
||||
bool horizontal, float padding);
|
||||
float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale);
|
||||
PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
|
||||
bool horizontal);
|
||||
void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale,
|
||||
const PlacementRange &placementRange, bool horizontal);
|
||||
|
||||
private:
|
||||
void *cTree;
|
||||
void *hTree;
|
||||
void *cTree;
|
||||
|
||||
public:
|
||||
const float tilePixelRatio;
|
||||
const float zoom;
|
||||
const float maxPlacementScale;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ GlyphRange getGlyphRange(char32_t glyph);
|
||||
|
||||
struct GlyphMetrics {
|
||||
operator bool() const {
|
||||
return width == 0 && height == 0 && advance == 0;
|
||||
return !(width == 0 && height == 0 && advance == 0);
|
||||
}
|
||||
|
||||
// Glyph metrics.
|
||||
@@ -35,7 +35,7 @@ struct Glyph {
|
||||
: rect(rect), metrics(metrics) {}
|
||||
|
||||
operator bool() const {
|
||||
return !metrics && !rect;
|
||||
return metrics || rect;
|
||||
}
|
||||
|
||||
const Rect<uint16_t> rect;
|
||||
@@ -44,18 +44,17 @@ struct Glyph {
|
||||
|
||||
typedef std::map<uint32_t, Glyph> GlyphPositions;
|
||||
|
||||
class GlyphPlacement {
|
||||
class PositionedGlyph {
|
||||
public:
|
||||
inline explicit GlyphPlacement(uint32_t face, uint32_t glyph, uint32_t x, uint32_t y)
|
||||
: face(face), glyph(glyph), x(x), y(y) {}
|
||||
inline explicit PositionedGlyph(uint32_t glyph, uint32_t x, uint32_t y)
|
||||
: glyph(glyph), x(x), y(y) {}
|
||||
|
||||
uint32_t face = 0;
|
||||
uint32_t glyph = 0;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
};
|
||||
|
||||
typedef std::vector<GlyphPlacement> Shaping;
|
||||
typedef std::vector<PositionedGlyph> Shaping;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
@@ -13,6 +14,7 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FileSource;
|
||||
|
||||
class SDFGlyph {
|
||||
public:
|
||||
@@ -30,17 +32,11 @@ public:
|
||||
void insert(uint32_t id, const SDFGlyph &glyph);
|
||||
const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
|
||||
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
|
||||
const Shaping getShaping(const std::u32string &string,
|
||||
const float &maxWidth,
|
||||
const float &lineHeight,
|
||||
const float &alignment,
|
||||
const float &verticalAlignment,
|
||||
const float &letterSpacing) const;
|
||||
void lineWrap(Shaping &shaping,
|
||||
const float &lineHeight,
|
||||
const float &maxWidth,
|
||||
const float &alignment,
|
||||
const float &verticalAlignment) const;
|
||||
const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
|
||||
float horizontalAlign, float verticalAlign, float justify,
|
||||
float spacing, const vec2<float> &translate) const;
|
||||
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
|
||||
float verticalAlign, float justify) const;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, std::string> bitmaps;
|
||||
@@ -51,7 +47,7 @@ private:
|
||||
|
||||
class GlyphPBF {
|
||||
public:
|
||||
GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange);
|
||||
GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource);
|
||||
|
||||
void parse(FontStack &stack);
|
||||
|
||||
@@ -67,13 +63,15 @@ private:
|
||||
// Manages Glyphrange PBF loading.
|
||||
class GlyphStore {
|
||||
public:
|
||||
GlyphStore(const std::string &glyphURL);
|
||||
GlyphStore(const std::shared_ptr<FileSource> &fileSource);
|
||||
|
||||
// Block until all specified GlyphRanges of the specified font stack are loaded.
|
||||
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
|
||||
|
||||
FontStack &getFontStack(const std::string &fontStack);
|
||||
|
||||
void setURL(const std::string &url);
|
||||
|
||||
private:
|
||||
// Loads an individual glyph range from the font stack and adds it to rangeSets
|
||||
std::shared_future<GlyphPBF &> loadGlyphRange(const std::string &fontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, GlyphRange range);
|
||||
@@ -81,9 +79,10 @@ private:
|
||||
FontStack &createFontStack(const std::string &fontStack);
|
||||
|
||||
public:
|
||||
const std::string glyphURL;
|
||||
std::string glyphURL;
|
||||
|
||||
private:
|
||||
const std::shared_ptr<FileSource> fileSource;
|
||||
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
|
||||
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
|
||||
std::mutex mtx;
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
#ifndef MBGL_TEXT_PLACEMENT
|
||||
#define MBGL_TEXT_PLACEMENT
|
||||
|
||||
#include <mbgl/text/collision.hpp>
|
||||
#include <mbgl/geometry/geometry.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/text/types.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class TextBucket;
|
||||
class StyleBucketText;
|
||||
struct Anchor;
|
||||
class StyleBucketSymbol;
|
||||
|
||||
class Placement {
|
||||
public:
|
||||
Placement(int8_t zoom);
|
||||
static Placement getIcon(Anchor &anchor, const Rect<uint16_t> &image, float iconBoxScale,
|
||||
const std::vector<Coordinate> &line, const StyleBucketSymbol &props);
|
||||
|
||||
void addFeature(TextBucket &bucket, const std::vector<Coordinate> &line,
|
||||
const StyleBucketText &info,
|
||||
const GlyphPositions &face,
|
||||
const Shaping &shaping);
|
||||
static Placement getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping,
|
||||
const GlyphPositions &face, float boxScale, bool horizontal,
|
||||
const std::vector<Coordinate> &line, const StyleBucketSymbol &props);
|
||||
|
||||
private:
|
||||
const int8_t zoom;
|
||||
Collision collision;
|
||||
const float zOffset;
|
||||
const float maxPlacementScale;
|
||||
static const float globalMinScale;
|
||||
|
||||
public:
|
||||
static const int tileExtent;
|
||||
static const int glyphSize;
|
||||
static const float minScale;
|
||||
GlyphBoxes boxes;
|
||||
PlacedGlyphs shapes;
|
||||
float minScale;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
typedef vec2<float> CollisionPoint;
|
||||
@@ -35,23 +37,23 @@ struct CollisionRect {
|
||||
// These are the glyph boxes that we want to have placed.
|
||||
struct GlyphBox {
|
||||
explicit GlyphBox() {}
|
||||
explicit GlyphBox(const CollisionRect &bbox, const CollisionRect &box,
|
||||
float minScale)
|
||||
: bbox(bbox), box(box), minScale(minScale) {}
|
||||
explicit GlyphBox(const CollisionRect &box, float minScale, float maxScale,
|
||||
const CollisionAnchor &anchor, bool rotate)
|
||||
: anchor(anchor),
|
||||
box(box),
|
||||
rotate(rotate),
|
||||
minScale(minScale),
|
||||
maxScale(maxScale) {}
|
||||
explicit GlyphBox(const CollisionRect &box,
|
||||
const CollisionAnchor &anchor,
|
||||
float minScale,
|
||||
float maxScale,
|
||||
float padding)
|
||||
: box(box), anchor(anchor), minScale(minScale), maxScale(maxScale), padding(padding) {}
|
||||
explicit GlyphBox(const CollisionRect &box,
|
||||
float minScale,
|
||||
float padding)
|
||||
: box(box), minScale(minScale), padding(padding) {}
|
||||
|
||||
CollisionAnchor anchor;
|
||||
CollisionRect bbox;
|
||||
CollisionRect box;
|
||||
bool rotate = false;
|
||||
CollisionAnchor anchor;
|
||||
float minScale = 0.0f;
|
||||
float maxScale = std::numeric_limits<float>::infinity();
|
||||
float padding = 0.0f;
|
||||
boost::optional<CollisionRect> hBox;
|
||||
};
|
||||
|
||||
typedef std::vector<GlyphBox> GlyphBoxes;
|
||||
@@ -61,19 +63,23 @@ typedef std::vector<GlyphBox> GlyphBoxes;
|
||||
struct PlacedGlyph {
|
||||
explicit PlacedGlyph(const vec2<float> &tl, const vec2<float> &tr,
|
||||
const vec2<float> &bl, const vec2<float> &br,
|
||||
const Rect<uint16_t> &tex, float angle, const GlyphBox &glyphBox)
|
||||
const Rect<uint16_t> &tex, float angle, const vec2<float> &anchor,
|
||||
float minScale, float maxScale)
|
||||
: tl(tl),
|
||||
tr(tr),
|
||||
bl(bl),
|
||||
br(br),
|
||||
tex(tex),
|
||||
angle(angle),
|
||||
glyphBox(glyphBox) {}
|
||||
anchor(anchor),
|
||||
minScale(minScale),
|
||||
maxScale(maxScale) {}
|
||||
|
||||
vec2<float> tl, tr, bl, br;
|
||||
Rect<uint16_t> tex;
|
||||
float angle;
|
||||
GlyphBox glyphBox;
|
||||
vec2<float> anchor;
|
||||
float minScale, maxScale;
|
||||
};
|
||||
|
||||
typedef std::vector<PlacedGlyph> PlacedGlyphs;
|
||||
@@ -81,9 +87,8 @@ typedef std::vector<PlacedGlyph> PlacedGlyphs;
|
||||
// These are the placed boxes contained in the rtree.
|
||||
struct PlacementBox {
|
||||
CollisionAnchor anchor;
|
||||
CollisionRect bbox;
|
||||
CollisionRect box;
|
||||
bool rotate = false;
|
||||
boost::optional<CollisionRect> hBox;
|
||||
PlacementRange placementRange = {{0.0f, 0.0f}};
|
||||
float placementScale = 0.0f;
|
||||
float maxScale = std::numeric_limits<float>::infinity();
|
||||
|
||||
45
include/mbgl/util/filesource.hpp
Normal file
45
include/mbgl/util/filesource.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef MBGL_UTIL_FILESOURCE
|
||||
#define MBGL_UTIL_FILESOURCE
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace uv {
|
||||
class loop;
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace platform {
|
||||
struct Response;
|
||||
}
|
||||
|
||||
enum class ResourceType : uint8_t {
|
||||
Unknown,
|
||||
Tile,
|
||||
Glyphs,
|
||||
Image,
|
||||
JSON
|
||||
};
|
||||
|
||||
class FileSource {
|
||||
public:
|
||||
FileSource();
|
||||
|
||||
void setBase(const std::string &value);
|
||||
const std::string &getBase() const;
|
||||
|
||||
void load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop = nullptr);
|
||||
|
||||
private:
|
||||
// Stores a URL that is used as a base for loading resources with relative path.
|
||||
std::string base;
|
||||
|
||||
// Stores the absolute path to the cache directory.
|
||||
const std::string cache;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,7 +5,8 @@ namespace mbgl {
|
||||
|
||||
template <typename T>
|
||||
struct Rect {
|
||||
explicit Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {}
|
||||
inline Rect() {}
|
||||
inline Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {}
|
||||
T x = 0, y = 0;
|
||||
T w = 0, h = 0;
|
||||
|
||||
@@ -14,7 +15,7 @@ struct Rect {
|
||||
return Rect(x * value, y * value, w * value, h * value);
|
||||
}
|
||||
|
||||
operator bool() const { return w == 0 || h == 0; }
|
||||
operator bool() const { return w != 0 && h != 0; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
namespace detail {
|
||||
const regex_impl::regex tokenRegex("\\{(\\w+)\\}");
|
||||
const regex_impl::regex tokenRegex("\\{([\\w-]+)\\}");
|
||||
const regex_impl::sregex_token_iterator tokensEnd = regex_impl::sregex_token_iterator();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,18 +16,37 @@
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace uv {
|
||||
|
||||
inline std::string cwd() {
|
||||
size_t max = 0;
|
||||
std::string dir;
|
||||
do {
|
||||
max += 256;
|
||||
dir.resize(max);
|
||||
uv_cwd(const_cast<char *>(dir.data()), &max);
|
||||
} while (max == dir.size());
|
||||
dir.resize(max - 1);
|
||||
return dir;
|
||||
}
|
||||
|
||||
class loop {
|
||||
public:
|
||||
inline loop() : l(uv_loop_new()) {}
|
||||
inline ~loop() { uv_loop_delete(l); }
|
||||
inline loop() {
|
||||
if (uv_loop_init(&l) != 0) {
|
||||
throw std::runtime_error("failed to initialize loop");
|
||||
}
|
||||
}
|
||||
|
||||
inline uv_loop_t *operator*() { return l; }
|
||||
inline ~loop() { uv_loop_close(&l); }
|
||||
|
||||
inline uv_loop_t *operator*() { return &l; }
|
||||
|
||||
private:
|
||||
uv_loop_t *l;
|
||||
uv_loop_t l;
|
||||
};
|
||||
|
||||
class mutex {
|
||||
|
||||
Submodule ios/mapbox-gl-cocoa updated: 6d29b1a2be...ced284d8a7
@@ -45,22 +45,13 @@ int main(int argc, char *argv[]) {
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
|
||||
// read default stylesheet from disk
|
||||
std::ifstream stylefile("./style.min.js");
|
||||
if (!stylefile.good()) {
|
||||
fprintf(stderr, "Cannot read style file\n");
|
||||
return 1;
|
||||
}
|
||||
std::stringstream stylejson;
|
||||
stylejson << stylefile.rdbuf();
|
||||
|
||||
view = new GLFWView();
|
||||
mbgl::Map map(*view);
|
||||
|
||||
// Load settings
|
||||
mbgl::Settings_JSON settings;
|
||||
map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
|
||||
map.setAngle(settings.angle);
|
||||
map.setBearing(settings.bearing);
|
||||
map.setDebug(settings.debug);
|
||||
|
||||
// Set access token if present
|
||||
@@ -72,13 +63,14 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Load style
|
||||
map.setStyleJSON(stylejson.str());
|
||||
const std::string style = std::string("file://") + uv::cwd() + std::string("/styles/bright/style.json");
|
||||
map.setStyleURL(style);
|
||||
|
||||
int ret = view->run();
|
||||
|
||||
// Save settings
|
||||
map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
|
||||
settings.angle = map.getAngle();
|
||||
settings.bearing = map.getBearing();
|
||||
settings.debug = map.getDebug();
|
||||
settings.save();
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
],
|
||||
'dependencies': [
|
||||
'../mapboxgl.gyp:mapboxgl',
|
||||
'../mapboxgl.gyp:copy_default_stylesheet',
|
||||
'../mapboxgl.gyp:copy_styles',
|
||||
'../mapboxgl.gyp:copy_certificate_bundle',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -32,5 +32,16 @@
|
||||
<string>0.0.1</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>mapboxgl</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -4,35 +4,102 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface URLHandler : NSObject
|
||||
@property (nonatomic) mbgl::Map *map;
|
||||
|
||||
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
|
||||
withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
|
||||
@end
|
||||
|
||||
@implementation URLHandler
|
||||
|
||||
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
|
||||
withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
|
||||
(void)replyEvent;
|
||||
|
||||
NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *param in [[url query] componentsSeparatedByString:@"&"]) {
|
||||
NSArray *parts = [param componentsSeparatedByString:@"="];
|
||||
if([parts count] < 2) continue;
|
||||
[params setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
|
||||
}
|
||||
|
||||
double latitude = 0, longitude = 0, zoom = 0, bearing = 0;
|
||||
bool hasCenter = false, hasZoom = false, hasBearing = false;
|
||||
|
||||
NSString *centerString = [params objectForKey:@"center"];
|
||||
if (centerString) {
|
||||
NSArray *latlon = [centerString componentsSeparatedByString:@","];
|
||||
if ([latlon count] == 2) {
|
||||
latitude = [[latlon objectAtIndex:0] doubleValue];
|
||||
longitude = [[latlon objectAtIndex:1] doubleValue];
|
||||
hasCenter = true;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *zoomString = [params objectForKey:@"zoom"];
|
||||
if (zoomString) {
|
||||
zoom = [zoomString doubleValue];
|
||||
hasZoom = true;
|
||||
}
|
||||
|
||||
NSString *bearingString = [params objectForKey:@"bearing"];
|
||||
if (bearingString) {
|
||||
bearing = [bearingString doubleValue];
|
||||
hasBearing = true;
|
||||
}
|
||||
|
||||
if ([self map]) {
|
||||
if (hasCenter && hasZoom) {
|
||||
[self map]->setLonLatZoom(longitude, latitude, zoom);
|
||||
} else if (hasCenter) {
|
||||
[self map]->setLonLat(longitude, latitude);
|
||||
} else if (hasZoom) {
|
||||
[self map]->setZoom(zoom);
|
||||
}
|
||||
|
||||
if (hasBearing) {
|
||||
[self map]->setBearing(bearing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
mbgl::Log::Set<mbgl::NSLogBackend>();
|
||||
|
||||
GLFWView view;
|
||||
mbgl::Map map(view);
|
||||
|
||||
URLHandler *handler = [[URLHandler alloc] init];
|
||||
[handler setMap:&map];
|
||||
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
|
||||
[appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
|
||||
|
||||
// Load settings
|
||||
mbgl::Settings_NSUserDefaults settings;
|
||||
map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
|
||||
map.setAngle(settings.angle);
|
||||
map.setBearing(settings.bearing);
|
||||
map.setDebug(settings.debug);
|
||||
|
||||
|
||||
// Set access token if present
|
||||
NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
|
||||
if ( ! accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work.");
|
||||
if (accessToken) map.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
|
||||
// Load style
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"style.min" ofType:@"js"];
|
||||
NSString *json = [NSString stringWithContentsOfFile:path
|
||||
encoding:[NSString defaultCStringEncoding]
|
||||
error:nil];
|
||||
map.setStyleJSON((std::string)[json cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
const std::string path([[[NSBundle mainBundle] pathForResource:@"style" ofType:@"json" inDirectory:@"styles/bright"] UTF8String]);
|
||||
|
||||
map.setStyleURL(std::string("file://") + path);
|
||||
|
||||
int ret = view.run();
|
||||
|
||||
// Save settings
|
||||
map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
|
||||
settings.angle = map.getAngle();
|
||||
settings.bearing = map.getBearing();
|
||||
settings.debug = map.getDebug();
|
||||
settings.save();
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
'mac_bundle': 1,
|
||||
'mac_bundle_resources': [
|
||||
'Icon.icns',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js'
|
||||
],
|
||||
'xcode_settings': {
|
||||
'SDKROOT': 'macosx',
|
||||
@@ -40,7 +39,8 @@
|
||||
'CLANG_ENABLE_OBJC_ARC': 'YES'
|
||||
},
|
||||
'dependencies': [
|
||||
'../mapboxgl.gyp:mapboxgl'
|
||||
'../mapboxgl.gyp:bundle_styles',
|
||||
'../mapboxgl.gyp:mapboxgl',
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
41
mapboxgl.gyp
41
mapboxgl.gyp
@@ -54,7 +54,7 @@
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'build_stylesheet',
|
||||
'target_name': 'bundle_styles',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
@@ -62,40 +62,39 @@
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Build Stylesheet',
|
||||
'inputs': [
|
||||
'bin/default.style.json',
|
||||
],
|
||||
'outputs': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js',
|
||||
],
|
||||
'action': ['<@(node)', 'bin/build-style.js', '<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)/bin']
|
||||
'action_name': 'Touch Stylesheet Directory',
|
||||
'inputs': ['styles'],
|
||||
'outputs': ['styles'],
|
||||
'action': ['touch', 'styles'], # required for xcode http://openradar.appspot.com/7232149
|
||||
}
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'sources': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js',
|
||||
'mac_bundle_resources': [
|
||||
'styles',
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
'target_name': 'copy_default_stylesheet',
|
||||
'target_name': 'copy_styles',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
'build_stylesheet'
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Touch Stylesheet Directory',
|
||||
'inputs': ['styles'],
|
||||
'outputs': ['styles'],
|
||||
'action': ['touch', 'styles'], # required for xcode http://openradar.appspot.com/7232149
|
||||
}
|
||||
],
|
||||
'copies': [
|
||||
{
|
||||
'files': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js',
|
||||
],
|
||||
'files': [ 'styles' ],
|
||||
'destination': '<(PRODUCT_DIR)'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'target_name': 'copy_default_stylesheet_fixtures',
|
||||
'target_name': 'copy_fixtures',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
@@ -103,9 +102,7 @@
|
||||
],
|
||||
'copies': [
|
||||
{
|
||||
'files': [
|
||||
'bin/default.style.json',
|
||||
],
|
||||
'files': [ 'styles' ],
|
||||
'destination': 'test/fixtures/style_parser'
|
||||
}
|
||||
]
|
||||
@@ -127,7 +124,6 @@
|
||||
'type': 'static_library',
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
'build_stylesheet',
|
||||
'shaders',
|
||||
],
|
||||
'sources': [
|
||||
@@ -195,7 +191,6 @@
|
||||
'type': 'static_library',
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
'build_stylesheet',
|
||||
'shaders',
|
||||
],
|
||||
'sources': [
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var base_dir = path.join(path.resolve('.'), 'test/fixtures/styles');
|
||||
|
||||
var files = fs.readdirSync(base_dir).filter(function(name) {
|
||||
return name.match(/\.info\.json$/);
|
||||
});
|
||||
|
||||
var html =
|
||||
'<style>\n' +
|
||||
' body { font-family: Helvetica; }\n' +
|
||||
' h2 a { color:white; text-decoration:none; }\n' +
|
||||
'</style>\n' +
|
||||
'<table>\n' +
|
||||
'<tr>\n' +
|
||||
' <th>Actual</th>\n' +
|
||||
' <th>Expected</th>\n' +
|
||||
' <th>Info</th>\n' +
|
||||
'</tr>\n';
|
||||
|
||||
var exitCode = 0;
|
||||
var failures = 0;
|
||||
|
||||
processFiles();
|
||||
|
||||
function processFiles() {
|
||||
if (!files.length) return done();
|
||||
|
||||
var name = files.shift();
|
||||
|
||||
var info = require(path.join(base_dir, name));
|
||||
|
||||
var keys = Object.keys(info);
|
||||
|
||||
processFileTest();
|
||||
|
||||
function processFileTest() {
|
||||
if (!keys.length) return processFiles();
|
||||
|
||||
var key = keys.shift();
|
||||
|
||||
var base = path.basename(name, '.info.json');
|
||||
|
||||
var actual = path.join(base_dir, base + '/' + key + '.actual.png');
|
||||
var expected = path.join(base_dir, base + '/' + key + '.expected.png');
|
||||
var diff = path.join(base_dir, base + '/' + key + '.diff.png');
|
||||
|
||||
var compare = spawn('compare', ['-metric', 'MAE', actual, expected, diff ]);
|
||||
var error = '';
|
||||
compare.stderr.on('data', function(data) {
|
||||
error += data.toString();
|
||||
});
|
||||
compare.on('exit', function(code, signal) {
|
||||
// The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar.
|
||||
if (code == 2) {
|
||||
writeResult(base, key, info[key], error.trim(), Infinity);
|
||||
exitCode = 2;
|
||||
} else {
|
||||
var match = error.match(/^\d+(?:\.\d+)?\s+\(([^\)]+)\)\s*$/);
|
||||
var difference = match ? parseFloat(match[1]) : Infinity;
|
||||
writeResult(base, key, info[key], match ? '' : error, difference);
|
||||
|
||||
}
|
||||
processFileTest();
|
||||
});
|
||||
compare.stdin.end();
|
||||
}
|
||||
}
|
||||
|
||||
function writeResult(base, key, info, error, difference) {
|
||||
var color = 'green';
|
||||
var allowedDifference = ('diff' in info) ? info.diff : 0.01;
|
||||
if (difference > allowedDifference) {
|
||||
color = 'red';
|
||||
if (exitCode < 1) {
|
||||
exitCode = 1;
|
||||
}
|
||||
failures++;
|
||||
}
|
||||
|
||||
html +=
|
||||
'<tr>\n' +
|
||||
' <td><img src="' + base + '/' + key + '.actual.png" onmouseover="this.src=\'' + base + '/' + key + '.expected.png\'" onmouseout="this.src=\'' + base + '/' + key + '.actual.png\'"></td>\n' +
|
||||
' <td><img src="' + base + '/' + key + '.expected.png" onmouseover="this.src=\'' + base + '/' + key + '.diff.png\'" onmouseout="this.src=\'' + base + '/' + key + '.expected.png\'"></td>\n' +
|
||||
' <td>\n' +
|
||||
' <h2 style="text-align:center; background:' + color + '"><a href="' + base + '.style.json">' + base + '/' + key + '</a></h2>\n' +
|
||||
(error ? ' <p>' + error + '</p>\n' : '') +
|
||||
' <ul>\n' +
|
||||
' <li>diff: <strong>' + difference + '</strong></li>\n' +
|
||||
' <li>zoom: <strong>' + (info.zoom || 0) + '</strong></li>\n' +
|
||||
' <li>center: <strong>' + (info.center || [0, 0]) + '</strong></li>\n' +
|
||||
' <li>bearing: <strong>' + (info.bearing || 0) + '</strong></li>\n' +
|
||||
' <li>width: <strong>' + (info.width || 512) + '</strong></li>\n' +
|
||||
' <li>height: <strong>' + (info.height || 512) + '</strong></li>\n' +
|
||||
' </ul>\n' +
|
||||
' </td>\n' +
|
||||
'</tr>\n'
|
||||
;
|
||||
}
|
||||
|
||||
function done() {
|
||||
html += "</table>\n";
|
||||
|
||||
fs.writeFileSync(path.join(base_dir, 'index.html'), html);
|
||||
console.warn('Results at: ' + path.join(base_dir, 'index.html'));
|
||||
if (failures) {
|
||||
console.warn('\x1B[1m\x1B[31m' + failures + ' ' + (failures == 1 ? 'image doesn\'t' : 'images don\'t') + ' match\x1B[39m\x1B[22m');
|
||||
} else {
|
||||
console.warn('\x1B[1m\x1B[32mAll images match\x1B[39m\x1B[22m');
|
||||
}
|
||||
|
||||
process.exit(exitCode);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
aws s3 cp test/fixtures/styles/ s3://mapbox-gl-testing/headless/$TRAVIS_JOB_NUMBER/ --acl public-read --recursive
|
||||
|
||||
echo http://mapbox-gl-testing.s3.amazonaws.com/headless/$TRAVIS_JOB_NUMBER/index.html
|
||||
@@ -1,18 +1,35 @@
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace mbgl;
|
||||
namespace mbgl {
|
||||
|
||||
void IconVertexBuffer::add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty) {
|
||||
const double IconVertexBuffer::angleFactor = 128.0 / M_PI;
|
||||
|
||||
size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom) {
|
||||
const size_t idx = index();
|
||||
void *data = addElement();
|
||||
|
||||
vertex_type *vertices = static_cast<vertex_type *>(data);
|
||||
vertices[0] = x;
|
||||
vertices[1] = y;
|
||||
int16_t *shorts = static_cast<int16_t *>(data);
|
||||
shorts[0] /* pos */ = x;
|
||||
shorts[1] /* pos */ = y;
|
||||
shorts[2] /* offset */ = std::round(ox * 64); // use 1/64 pixels for placement
|
||||
shorts[3] /* offset */ = std::round(oy * 64);
|
||||
|
||||
uint8_t *ubytes = static_cast<uint8_t *>(data);
|
||||
ubytes[8] /* labelminzoom */ = labelminzoom * 10;
|
||||
ubytes[9] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160.
|
||||
ubytes[10] /* maxzoom */ = std::fmin(maxzoom, 25) * 10; // 1/10 zoom levels: z16 == 160.
|
||||
ubytes[11] /* angle */ = (int16_t)std::round(angle * angleFactor) % 256;
|
||||
ubytes[12] /* rangeend */ = util::max((int16_t)std::round(range[0] * angleFactor), (int16_t)0) % 256;
|
||||
ubytes[13] /* rangestart */ = util::min((int16_t)std::round(range[1] * angleFactor), (int16_t)255) % 256;
|
||||
|
||||
shorts[8] /* tex */ = tx;
|
||||
shorts[9] /* tex */ = ty;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
uint16_t *texture = static_cast<uint16_t *>(data);
|
||||
texture[2] = tx;
|
||||
texture[3] = ty;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
#include <mbgl/geometry/interpolate.hpp>
|
||||
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
const float minScale = 0.5f;
|
||||
const std::array<std::vector<float>, 4> minScaleArrays = {{
|
||||
/*1:*/ { minScale },
|
||||
/*2:*/ { minScale, 2 },
|
||||
/*4:*/ { minScale, 4, 2, 4 },
|
||||
/*8:*/ { minScale, 8, 4, 8, 2, 8, 4, 8 }
|
||||
}};
|
||||
|
||||
|
||||
Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
|
||||
float minScale, int start) {
|
||||
const float /*minScale*/, float maxScale, const float tilePixelRatio,
|
||||
const int start) {
|
||||
|
||||
maxScale = std::round(std::fmax(std::fmin(8.0f, maxScale / 2.0f), 1.0f));
|
||||
spacing *= tilePixelRatio / maxScale;
|
||||
const size_t index = util::clamp<size_t>(std::floor(std::log(maxScale) / std::log(2)), 0, minScaleArrays.size() - 1);
|
||||
const std::vector<float> &minScales = minScaleArrays[index];
|
||||
const size_t len = minScales.size();
|
||||
|
||||
float distance = 0.0f;
|
||||
float markedDistance = 0.0f;
|
||||
int added = start;
|
||||
@@ -23,9 +44,7 @@ Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
|
||||
|
||||
float t = (markedDistance - distance) / segmentDist,
|
||||
x = util::interp(a.x, b.x, t), y = util::interp(a.y, b.y, t),
|
||||
s = added % 8 == 0 ? minScale : added % 4 == 0
|
||||
? 2
|
||||
: added % 2 == 0 ? 4 : 8;
|
||||
s = minScales[added % len];
|
||||
|
||||
if (x >= 0 && x < 4096 && y >= 0 && y < 4096) {
|
||||
points.emplace_back(x, y, angle, s, i);
|
||||
|
||||
@@ -88,27 +88,6 @@ void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, con
|
||||
}
|
||||
}
|
||||
|
||||
void draw_circle(uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y,
|
||||
const int width, const int height, const float blur,
|
||||
const uint8_t r = 0xFF, const uint8_t g = 0xFF, const uint8_t b = 0xFF) {
|
||||
const int sprite_stride = dst_stride;
|
||||
const int radius = util::min(width, height);
|
||||
for (int y = 0; y < height; y++) {
|
||||
const int img_y = (dst_y + y) * sprite_stride + dst_x;
|
||||
for (int x = 0; x < height; x++) {
|
||||
const float dist = util::length(float(x) / radius - 0.5f, float(y) / radius - 0.5f);
|
||||
const float t = util::smoothstep(0.5f, 0.5f - blur, dist);
|
||||
const uint8_t alpha = t * 255;
|
||||
|
||||
uint32_t color = (uint32_t(r * t) << 0) |
|
||||
(uint32_t(g * t) << 8) |
|
||||
(uint32_t(b * t) << 16) |
|
||||
(uint32_t(alpha) << 24);
|
||||
dst[img_y + x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t width, size_t height) {
|
||||
// We have to allocate a new area in the bin, and store an empty image in it.
|
||||
// Add a 1px border around every image.
|
||||
@@ -125,44 +104,6 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t width, size_t hei
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::getIcon(const int size, const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
auto rect_it = images.find(name);
|
||||
if (rect_it != images.end()) {
|
||||
return rect_it->second;
|
||||
}
|
||||
|
||||
Rect<dimension> rect = allocateImage(size, size);
|
||||
if (rect.w == 0) {
|
||||
if (debug::spriteWarnings) {
|
||||
fprintf(stderr, "[WARNING] sprite atlas bitmap overflow\n");
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
images.emplace(name, rect);
|
||||
|
||||
allocate();
|
||||
|
||||
// Draw an antialiased circle.
|
||||
draw_circle(
|
||||
reinterpret_cast<uint32_t *>(data),
|
||||
width * pixelRatio,
|
||||
rect.x * pixelRatio,
|
||||
rect.y * pixelRatio,
|
||||
size * pixelRatio,
|
||||
size * pixelRatio,
|
||||
1.0f / size / pixelRatio
|
||||
);
|
||||
|
||||
uninitialized.emplace(name);
|
||||
|
||||
dirty = true;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string &name, const Sprite &sprite) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
@@ -191,6 +132,12 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string &name, cons
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::waitForImage(const std::string &name, const Sprite &sprite) {
|
||||
sprite.waitUntilLoaded();
|
||||
return getImage(name, sprite);
|
||||
}
|
||||
|
||||
void SpriteAtlas::allocate() {
|
||||
if (!data) {
|
||||
dimension w = static_cast<dimension>(width * pixelRatio);
|
||||
|
||||
129
src/map/map.cpp
129
src/map/map.cpp
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/util/transition.hpp>
|
||||
@@ -10,25 +11,36 @@
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/style/style_layer_group.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/util/texturepool.hpp>
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
Map::Map(View& view)
|
||||
: view(view),
|
||||
: loop(std::make_shared<uv::loop>()),
|
||||
view(view),
|
||||
transform(view),
|
||||
fileSource(std::make_shared<FileSource>()),
|
||||
style(std::make_shared<Style>()),
|
||||
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
|
||||
glyphStore(std::make_shared<GlyphStore>(fileSource)),
|
||||
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
|
||||
texturepool(std::make_shared<Texturepool>()),
|
||||
painter(*this),
|
||||
loop(std::make_shared<uv::loop>()) {
|
||||
painter(*this) {
|
||||
|
||||
view.initialize(this);
|
||||
|
||||
@@ -160,7 +172,7 @@ void Map::render(uv_async_t *async) {
|
||||
void Map::terminate(uv_async_t *async) {
|
||||
// Closes all open handles on the loop. This means that the loop will automatically terminate.
|
||||
uv_loop_t *loop = static_cast<uv_loop_t *>(async->data);
|
||||
uv_walk(loop, [](uv_handle_t *handle, void *arg) {
|
||||
uv_walk(loop, [](uv_handle_t *handle, void */*arg*/) {
|
||||
if (!uv_is_closing(handle)) {
|
||||
uv_close(handle, NULL);
|
||||
}
|
||||
@@ -175,9 +187,30 @@ void Map::setup() {
|
||||
painter.setup();
|
||||
}
|
||||
|
||||
void Map::setStyleJSON(std::string newStyleJSON) {
|
||||
void Map::setStyleURL(const std::string &url) {
|
||||
fileSource->load(ResourceType::JSON, url, [&](platform::Response *res) {
|
||||
if (res->code == 200) {
|
||||
// Calculate the base
|
||||
const size_t pos = url.rfind('/');
|
||||
std::string base = "";
|
||||
if (pos != std::string::npos) {
|
||||
base = url.substr(0, pos + 1);
|
||||
}
|
||||
|
||||
this->setStyleJSON(res->body, base);
|
||||
} else {
|
||||
Log::Error(Event::Setup, "loading style failed: %d (%s)", res->code, res->error_message.c_str());
|
||||
}
|
||||
}, loop);
|
||||
}
|
||||
|
||||
|
||||
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
|
||||
styleJSON.swap(newStyleJSON);
|
||||
sprite.reset();
|
||||
style->loadJSON((const uint8_t *)styleJSON.c_str());
|
||||
fileSource->setBase(base);
|
||||
glyphStore->setURL(style->glyph_url);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -193,6 +226,17 @@ std::string Map::getAccessToken() const {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sprite> Map::getSprite() {
|
||||
const float pixelRatio = state.getPixelRatio();
|
||||
const std::string &sprite_url = style->getSpriteURL();
|
||||
if (!sprite || sprite->pixelRatio != pixelRatio) {
|
||||
sprite = Sprite::Create(sprite_url, pixelRatio, fileSource);
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Size
|
||||
|
||||
// Note: This function is called from another thread. Make sure you only call threadsafe functions!
|
||||
@@ -333,20 +377,20 @@ void Map::rotateBy(double sx, double sy, double ex, double ey, double duration)
|
||||
}
|
||||
|
||||
// Note: This function is called from another thread. Make sure you only call threadsafe functions!
|
||||
void Map::setAngle(double angle, double duration) {
|
||||
transform.setAngle(angle, duration * 1_second);
|
||||
void Map::setBearing(double degrees, double duration) {
|
||||
transform.setAngle(-degrees * M_PI / 180, duration * 1_second);
|
||||
update();
|
||||
}
|
||||
|
||||
// Note: This function is called from another thread. Make sure you only call threadsafe functions!
|
||||
void Map::setAngle(double angle, double cx, double cy) {
|
||||
transform.setAngle(angle, cx, cy);
|
||||
void Map::setBearing(double degrees, double cx, double cy) {
|
||||
transform.setAngle(-degrees * M_PI / 180, cx, cy);
|
||||
update();
|
||||
}
|
||||
|
||||
// Note: This function is called from another thread. Make sure you only call threadsafe functions!
|
||||
double Map::getAngle() const {
|
||||
return transform.getAngle();
|
||||
double Map::getBearing() const {
|
||||
return -transform.getAngle() / M_PI * 180;
|
||||
}
|
||||
|
||||
// Note: This function is called from another thread. Make sure you only call threadsafe functions!
|
||||
@@ -421,13 +465,13 @@ void Map::updateSources() {
|
||||
updateSources(style->layers);
|
||||
|
||||
// Then, construct or destroy the actual source object, depending on enabled state.
|
||||
for (const std::shared_ptr<StyleSource> &source : activeSources) {
|
||||
if (source->enabled) {
|
||||
if (!source->source) {
|
||||
source->source = std::make_shared<Source>(*source, getAccessToken());
|
||||
for (const std::shared_ptr<StyleSource> &style_source : activeSources) {
|
||||
if (style_source->enabled) {
|
||||
if (!style_source->source) {
|
||||
style_source->source = std::make_shared<Source>(style_source->info, getAccessToken());
|
||||
}
|
||||
} else {
|
||||
source->source.reset();
|
||||
style_source->source.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,24 +538,6 @@ void Map::prepare() {
|
||||
bool dimensionsChanged = oldState.getFramebufferWidth() != state.getFramebufferWidth() ||
|
||||
oldState.getFramebufferHeight() != state.getFramebufferHeight();
|
||||
|
||||
if (pixelRatioChanged) {
|
||||
style->sprite = std::make_shared<Sprite>(*this, state.getPixelRatio());
|
||||
if (style->sprite_url.size()) {
|
||||
style->sprite->load(style->sprite_url);
|
||||
}
|
||||
spriteAtlas->resize(state.getPixelRatio());
|
||||
}
|
||||
|
||||
// Create a new glyph store object in case the glyph URL changed.
|
||||
// TODO: Move this to a less frequently called place; we only need to do this when the
|
||||
// stylesheet changes.
|
||||
if (glyphStore && glyphStore->glyphURL != style->glyph_url) {
|
||||
glyphStore.reset();
|
||||
}
|
||||
if (!glyphStore && style->glyph_url.size()) {
|
||||
glyphStore = std::make_shared<GlyphStore>(style->glyph_url);
|
||||
}
|
||||
|
||||
if (pixelRatioChanged || dimensionsChanged) {
|
||||
painter.clearFramebuffers();
|
||||
}
|
||||
@@ -521,9 +547,8 @@ void Map::prepare() {
|
||||
style->updateProperties(state.getNormalizedZoom(), animationTime);
|
||||
|
||||
// Allow the sprite atlas to potentially pull new sprite images if needed.
|
||||
if (style->sprite && style->sprite->isLoaded()) {
|
||||
spriteAtlas->update(*style->sprite);
|
||||
}
|
||||
spriteAtlas->resize(state.getPixelRatio());
|
||||
spriteAtlas->update(*getSprite());
|
||||
|
||||
updateTiles();
|
||||
}
|
||||
@@ -557,8 +582,6 @@ void Map::render() {
|
||||
source->source->finishRender(painter);
|
||||
}
|
||||
|
||||
painter.renderMatte();
|
||||
|
||||
// Schedule another rerender when we definitely need a next frame.
|
||||
if (transform.needsTransition() || style->hasTransitions()) {
|
||||
update();
|
||||
@@ -637,7 +660,7 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (layer_desc->id == "background") {
|
||||
} else if (layer_desc->type == StyleLayerType::Background) {
|
||||
// This layer defines the background color.
|
||||
} else {
|
||||
// This is a singular layer.
|
||||
@@ -653,6 +676,20 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) {
|
||||
|
||||
StyleSource &style_source = *layer_desc->bucket->style_source;
|
||||
|
||||
// Skip this layer if there is no data.
|
||||
if (!style_source.source) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip this layer if it's outside the range of min/maxzoom.
|
||||
// This may occur when there /is/ a bucket created for this layer, but the min/max-zoom
|
||||
// is set to a fractional value, or value that is larger than the source maxzoom.
|
||||
const double zoom = state.getZoom();
|
||||
if (layer_desc->bucket->min_zoom > zoom ||
|
||||
layer_desc->bucket->max_zoom <= zoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort early if we can already deduce from the bucket type that
|
||||
// we're not going to render anything anyway during this pass.
|
||||
switch (layer_desc->type) {
|
||||
@@ -663,13 +700,9 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) {
|
||||
if (pass == Opaque) return;
|
||||
if (!layer_desc->getProperties<LineProperties>().isVisible()) return;
|
||||
break;
|
||||
case StyleLayerType::Icon:
|
||||
case StyleLayerType::Symbol:
|
||||
if (pass == Opaque) return;
|
||||
if (!layer_desc->getProperties<IconProperties>().isVisible()) return;
|
||||
break;
|
||||
case StyleLayerType::Text:
|
||||
if (pass == Opaque) return;
|
||||
if (!layer_desc->getProperties<TextProperties>().isVisible()) return;
|
||||
if (!layer_desc->getProperties<SymbolProperties>().isVisible()) return;
|
||||
break;
|
||||
case StyleLayerType::Raster:
|
||||
if (pass == Translucent) return;
|
||||
@@ -684,8 +717,6 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) {
|
||||
<< layer_desc->type << ")" << std::endl;
|
||||
}
|
||||
|
||||
if (style_source.source) {
|
||||
style_source.source->render(painter, layer_desc);
|
||||
}
|
||||
style_source.source->render(painter, layer_desc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
using namespace mbgl;
|
||||
|
||||
|
||||
RasterTileData::RasterTileData(Tile::ID id, Map &map, const std::string url)
|
||||
: TileData(id, map, url),
|
||||
RasterTileData::RasterTileData(Tile::ID id, Map &map, const SourceInfo &source)
|
||||
: TileData(id, map, source),
|
||||
bucket(map.getTexturepool()) {
|
||||
}
|
||||
|
||||
@@ -29,6 +29,6 @@ void RasterTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_
|
||||
bucket.render(painter, layer_desc, id);
|
||||
}
|
||||
|
||||
bool RasterTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
|
||||
bool RasterTileData::hasData(std::shared_ptr<StyleLayer> /*layer_desc*/) const {
|
||||
return bucket.hasData();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/texturepool.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
@@ -18,21 +17,14 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
Source::Source(StyleSource style_source, const std::string &access_token)
|
||||
: type(style_source.type),
|
||||
url(normalizeSourceURL(style_source.url, access_token)),
|
||||
tile_size(style_source.tile_size),
|
||||
min_zoom(style_source.min_zoom),
|
||||
max_zoom(style_source.max_zoom) {}
|
||||
|
||||
Source::Source(SourceType type, const std::string &url,
|
||||
uint32_t tile_size, uint32_t min_zoom, uint32_t max_zoom,
|
||||
const std::string &access_token)
|
||||
: type(type),
|
||||
url(normalizeSourceURL(url, access_token)),
|
||||
tile_size(tile_size),
|
||||
min_zoom(min_zoom),
|
||||
max_zoom(max_zoom) {}
|
||||
Source::Source(SourceInfo info, const std::string &access_token)
|
||||
: info(
|
||||
info.type,
|
||||
normalizeSourceURL(info.url, access_token),
|
||||
info.tile_size,
|
||||
info.min_zoom,
|
||||
info.max_zoom
|
||||
) {}
|
||||
|
||||
std::string Source::normalizeSourceURL(const std::string &url, const std::string &access_token) {
|
||||
const std::string t = "mapbox://";
|
||||
@@ -153,18 +145,10 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) {
|
||||
|
||||
if (!new_tile.data) {
|
||||
// If we don't find working tile data, we're just going to load it.
|
||||
const std::string formed_url = util::replaceTokens(url, [&](const std::string &token) -> std::string {
|
||||
if (token == "z") return std::to_string(normalized_id.z);
|
||||
if (token == "x") return std::to_string(normalized_id.x);
|
||||
if (token == "y") return std::to_string(normalized_id.y);
|
||||
if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : "");
|
||||
return "";
|
||||
});
|
||||
|
||||
if (type == SourceType::Vector) {
|
||||
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, formed_url);
|
||||
} else if (type == SourceType::Raster) {
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, formed_url);
|
||||
if (info.type == SourceType::Vector) {
|
||||
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, info);
|
||||
} else if (info.type == SourceType::Raster) {
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, info);
|
||||
} else {
|
||||
throw std::runtime_error("source type not implemented");
|
||||
}
|
||||
@@ -232,14 +216,14 @@ bool Source::updateTiles(Map &map) {
|
||||
|
||||
// Figure out what tiles we need to load
|
||||
int32_t clamped_zoom = map.getState().getIntegerZoom();
|
||||
if (clamped_zoom > max_zoom) clamped_zoom = max_zoom;
|
||||
if (clamped_zoom < min_zoom) clamped_zoom = min_zoom;
|
||||
if (clamped_zoom > info.max_zoom) clamped_zoom = info.max_zoom;
|
||||
if (clamped_zoom < info.min_zoom) clamped_zoom = info.min_zoom;
|
||||
|
||||
int32_t max_covering_zoom = clamped_zoom + 1;
|
||||
if (max_covering_zoom > max_zoom) max_covering_zoom = max_zoom;
|
||||
if (max_covering_zoom > info.max_zoom) max_covering_zoom = info.max_zoom;
|
||||
|
||||
int32_t min_covering_zoom = clamped_zoom - 10;
|
||||
if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom;
|
||||
if (min_covering_zoom < info.min_zoom) min_covering_zoom = info.min_zoom;
|
||||
|
||||
// Map four viewport corners to pixel coordinates
|
||||
box box = map.getState().cornersToBox(clamped_zoom);
|
||||
@@ -379,7 +363,7 @@ void _scanTriangle(const mbgl::vec2<double> a, const mbgl::vec2<double> b, const
|
||||
}
|
||||
|
||||
double Source::getZoom(const TransformState &state) const {
|
||||
double offset = log(util::tileSize / tile_size) / log(2);
|
||||
double offset = log(util::tileSize / info.tile_size) / log(2);
|
||||
offset += (state.getPixelRatio() > 1.0 ? 1 :0);
|
||||
return state.getZoom() + offset;
|
||||
}
|
||||
@@ -387,7 +371,7 @@ double Source::getZoom(const TransformState &state) const {
|
||||
std::forward_list<mbgl::Tile::ID> Source::covering_tiles(const TransformState &state, int32_t clamped_zoom, const box& points) {
|
||||
int32_t dim = std::pow(2, clamped_zoom);
|
||||
std::forward_list<mbgl::Tile::ID> tiles;
|
||||
bool is_raster = (type == SourceType::Raster);
|
||||
bool is_raster = (info.type == SourceType::Raster);
|
||||
double search_zoom = getZoom(state);
|
||||
|
||||
auto scanLine = [&tiles, clamped_zoom, is_raster, search_zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
@@ -20,76 +21,98 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t
|
||||
pixelRatio(pixelRatio) {
|
||||
}
|
||||
|
||||
Sprite::Sprite(Map &map, float pixelRatio)
|
||||
: pixelRatio(pixelRatio),
|
||||
raster(),
|
||||
map(map),
|
||||
loaded(false) {
|
||||
std::shared_ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource) {
|
||||
std::shared_ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
|
||||
sprite->load(fileSource);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
void Sprite::load(const std::string& base_url) {
|
||||
loaded = false;
|
||||
url = base_url;
|
||||
Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio)
|
||||
: valid(base_url.length() > 0),
|
||||
pixelRatio(pixelRatio),
|
||||
spriteURL(base_url + (pixelRatio > 1 ? "@2x" : "") + ".png"),
|
||||
jsonURL(base_url + (pixelRatio > 1 ? "@2x" : "") + ".json"),
|
||||
raster(),
|
||||
loadedImage(false),
|
||||
loadedJSON(false),
|
||||
future(promise.get_future()) {
|
||||
}
|
||||
|
||||
void Sprite::waitUntilLoaded() const {
|
||||
future.wait();
|
||||
}
|
||||
|
||||
Sprite::operator bool() const {
|
||||
return valid && isLoaded() && !pos.empty();
|
||||
}
|
||||
|
||||
|
||||
// Note: This is a separate function that must be called exactly once after creation
|
||||
// The reason this isn't part of the constructor is that calling shared_from_this() in
|
||||
// the constructor fails.
|
||||
void Sprite::load(const std::shared_ptr<FileSource> &fileSource) {
|
||||
if (!valid) {
|
||||
// Treat a non-existent sprite as a successfully loaded empty sprite.
|
||||
loadedImage = true;
|
||||
loadedJSON = true;
|
||||
promise.set_value();
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sprite> sprite = shared_from_this();
|
||||
|
||||
std::string suffix = (pixelRatio > 1 ? "@2x" : "");
|
||||
|
||||
platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) {
|
||||
fileSource->load(ResourceType::JSON, jsonURL, [sprite](platform::Response *res) {
|
||||
if (res->code == 200) {
|
||||
sprite->body.swap(res->body);
|
||||
sprite->asyncParseJSON();
|
||||
sprite->parseJSON();
|
||||
sprite->complete();
|
||||
} else {
|
||||
fprintf(stderr, "failed to load sprite info\n");
|
||||
fprintf(stderr, "error %d: %s\n", res->code, res->error_message.c_str());
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res->code, res->error_message.c_str());
|
||||
if (!sprite->future.valid()) {
|
||||
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
|
||||
}
|
||||
}
|
||||
}, map.getLoop());
|
||||
});
|
||||
|
||||
platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) {
|
||||
fileSource->load(ResourceType::Image, spriteURL, [sprite](platform::Response *res) {
|
||||
if (res->code == 200) {
|
||||
sprite->image.swap(res->body);
|
||||
sprite->asyncParseImage();
|
||||
sprite->image.swap(res->body);
|
||||
sprite->parseImage();
|
||||
sprite->complete();
|
||||
} else {
|
||||
fprintf(stderr, "failed to load sprite image\n");
|
||||
fprintf(stderr, "error %d: %s\n", res->code, res->error_message.c_str());
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res->code, res->error_message.c_str());
|
||||
if (!sprite->future.valid()) {
|
||||
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
|
||||
}
|
||||
}
|
||||
}, map.getLoop());
|
||||
});
|
||||
}
|
||||
|
||||
void Sprite::complete(std::shared_ptr<Sprite> &sprite) {
|
||||
const bool raster = bool(sprite->raster);
|
||||
const bool json = bool(sprite->pos.size());
|
||||
if (raster && json && !sprite->loaded) {
|
||||
sprite->loaded = true;
|
||||
sprite->map.update();
|
||||
Log::Info(Event::Sprite, "loaded %s", sprite->url.c_str());
|
||||
void Sprite::complete() {
|
||||
if (loadedImage && loadedJSON) {
|
||||
Log::Info(Event::Sprite, "loaded %s", spriteURL.c_str());
|
||||
promise.set_value();
|
||||
}
|
||||
}
|
||||
|
||||
bool Sprite::isLoaded() const {
|
||||
return loaded;
|
||||
return loadedImage && loadedJSON;
|
||||
}
|
||||
|
||||
void Sprite::asyncParseImage() {
|
||||
new uv::work<std::shared_ptr<Sprite>>(map.getLoop(), parseImage, complete, shared_from_this());
|
||||
void Sprite::parseImage() {
|
||||
raster = std::make_unique<util::Image>(image);
|
||||
image.clear();
|
||||
loadedImage = true;
|
||||
}
|
||||
|
||||
void Sprite::asyncParseJSON() {
|
||||
new uv::work<std::shared_ptr<Sprite>>(map.getLoop(), parseJSON, complete, shared_from_this());
|
||||
}
|
||||
|
||||
void Sprite::parseImage(std::shared_ptr<Sprite> &sprite) {
|
||||
sprite->raster = std::make_unique<util::Image>(sprite->image);
|
||||
sprite->image.clear();
|
||||
}
|
||||
|
||||
void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
|
||||
void Sprite::parseJSON() {
|
||||
rapidjson::Document d;
|
||||
d.Parse<0>(sprite->body.c_str());
|
||||
sprite->body.clear();
|
||||
|
||||
if (d.IsObject()) {
|
||||
std::unordered_map<std::string, SpritePosition> pos;
|
||||
d.Parse<0>(body.c_str());
|
||||
body.clear();
|
||||
|
||||
if (d.HasParseError()) {
|
||||
Log::Warning(Event::Sprite, "sprite JSON is invalid");
|
||||
} else if (d.IsObject()) {
|
||||
for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) {
|
||||
const std::string& name = itr->name.GetString();
|
||||
const rapidjson::Value& value = itr->value;
|
||||
@@ -109,9 +132,11 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
|
||||
pos.emplace(name, SpritePosition { x, y, width, height, pixelRatio });
|
||||
}
|
||||
}
|
||||
|
||||
sprite->pos.swap(pos);
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "sprite JSON root is not an object");
|
||||
}
|
||||
|
||||
loadedJSON = true;
|
||||
}
|
||||
|
||||
const SpritePosition &Sprite::getSpritePosition(const std::string& name) const {
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TileData::TileData(Tile::ID id, Map &map, const std::string url)
|
||||
TileData::TileData(Tile::ID id, Map &map, const SourceInfo &source)
|
||||
: id(id),
|
||||
state(State::initial),
|
||||
map(map),
|
||||
url(url),
|
||||
source(source),
|
||||
url(util::replaceTokens(source.url, [&](const std::string &token) -> std::string {
|
||||
if (token == "z") return std::to_string(id.z);
|
||||
if (token == "x") return std::to_string(id.x);
|
||||
if (token == "y") return std::to_string(id.y);
|
||||
if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : "");
|
||||
return "";
|
||||
})),
|
||||
debugBucket(debugFontBuffer) {
|
||||
// Initialize tile debug coordinates
|
||||
const std::string str = util::sprintf<32>("%d/%d/%d", id.z, id.x, id.y);
|
||||
@@ -29,7 +39,7 @@ void TileData::request() {
|
||||
|
||||
// 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](platform::Response *res) {
|
||||
map.getFileSource()->load(ResourceType::Tile, 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
|
||||
@@ -46,7 +56,7 @@ void TileData::request() {
|
||||
fprintf(stderr, "[%s] tile loading failed: %d, %s\n", tile->url.c_str(), res->code, res->error_message.c_str());
|
||||
#endif
|
||||
}
|
||||
}, map.getLoop());
|
||||
});
|
||||
}
|
||||
|
||||
void TileData::cancel() {
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include <mbgl/style/style_layer_group.hpp>
|
||||
#include <mbgl/renderer/fill_bucket.hpp>
|
||||
#include <mbgl/renderer/line_bucket.hpp>
|
||||
#include <mbgl/renderer/icon_bucket.hpp>
|
||||
#include <mbgl/renderer/text_bucket.hpp>
|
||||
#include <mbgl/renderer/symbol_bucket.hpp>
|
||||
#include <mbgl/renderer/raster_bucket.hpp>
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
@@ -35,14 +34,16 @@ TileParser::TileParser(const std::string &data, VectorTileData &tile,
|
||||
const std::shared_ptr<const Style> &style,
|
||||
const std::shared_ptr<GlyphAtlas> &glyphAtlas,
|
||||
const std::shared_ptr<GlyphStore> &glyphStore,
|
||||
const std::shared_ptr<SpriteAtlas> &spriteAtlas)
|
||||
const std::shared_ptr<SpriteAtlas> &spriteAtlas,
|
||||
const std::shared_ptr<Sprite> &sprite)
|
||||
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
|
||||
tile(tile),
|
||||
style(style),
|
||||
glyphAtlas(glyphAtlas),
|
||||
glyphStore(glyphStore),
|
||||
spriteAtlas(spriteAtlas),
|
||||
placement(tile.id.z) {
|
||||
sprite(sprite),
|
||||
collision(tile.id.z, 4096, tile.source.tile_size, tile.depth) {
|
||||
}
|
||||
|
||||
void TileParser::parse() {
|
||||
@@ -51,21 +52,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<uint32_t, SDFGlyph> &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<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf);
|
||||
face.emplace(chr, Glyph{rect, sdf.metrics});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
|
||||
if (!group) {
|
||||
return;
|
||||
@@ -108,6 +94,10 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip this bucket if we are to not render this
|
||||
if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source.max_zoom) return nullptr;
|
||||
if (tile.id.z >= std::ceil(bucket_desc->max_zoom)) return nullptr;
|
||||
|
||||
auto layer_it = vector_data.layers.find(bucket_desc->source_layer);
|
||||
if (layer_it != vector_data.layers.end()) {
|
||||
const VectorTileLayer &layer = layer_it->second;
|
||||
@@ -115,10 +105,8 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu
|
||||
return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketFill>());
|
||||
} else if (bucket_desc->render.is<StyleBucketLine>()) {
|
||||
return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketLine>());
|
||||
} else if (bucket_desc->render.is<StyleBucketIcon>()) {
|
||||
return createIconBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketIcon>());
|
||||
} else if (bucket_desc->render.is<StyleBucketText>()) {
|
||||
return createTextBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketText>());
|
||||
} else if (bucket_desc->render.is<StyleBucketSymbol>()) {
|
||||
return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketSymbol>());
|
||||
} else if (bucket_desc->render.is<StyleBucketRaster>()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
@@ -136,7 +124,7 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu
|
||||
}
|
||||
|
||||
template <class Bucket>
|
||||
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())
|
||||
@@ -153,103 +141,20 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer,
|
||||
}
|
||||
}
|
||||
|
||||
template <class Bucket, typename... Args>
|
||||
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>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill) {
|
||||
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, fill);
|
||||
addBucketFeatures(bucket, layer, filter);
|
||||
addBucketGeometries(bucket, layer, filter);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line) {
|
||||
std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, line);
|
||||
addBucketFeatures(bucket, layer, filter);
|
||||
addBucketGeometries(bucket, layer, filter);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon) {
|
||||
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(tile.iconVertexBuffer, icon);
|
||||
addBucketFeatures(bucket, layer, filter, *spriteAtlas);
|
||||
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol) {
|
||||
std::unique_ptr<SymbolBucket> bucket = std::make_unique<SymbolBucket>(symbol, collision);
|
||||
bucket->addFeatures(layer, filter, tile.id, *spriteAtlas, *sprite, *glyphAtlas, *glyphStore);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> 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<TextBucket> bucket = std::make_unique<TextBucket>(
|
||||
tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement);
|
||||
|
||||
util::utf8_to_utf32 ucs4conv;
|
||||
|
||||
std::vector<std::pair<std::u32string, pbf>> labels;
|
||||
|
||||
// Determine and load glyph ranges
|
||||
{
|
||||
std::set<GlyphRange> 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::ctype<char>>(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<std::u32string, pbf> &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);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/transition.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <cstdio>
|
||||
|
||||
@@ -96,8 +98,8 @@ void Transform::setLonLat(const double lon, const double lat, const timestamp du
|
||||
uv::writelock lock(mtx);
|
||||
|
||||
const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999);
|
||||
double xn = -std::round(lon * Bc);
|
||||
double yn = std::round(0.5 * Cc * std::log((1 + f) / (1 - f)));
|
||||
double xn = -lon * Bc;
|
||||
double yn = 0.5 * Cc * std::log((1 + f) / (1 - f));
|
||||
|
||||
_setScaleXY(current.scale, xn, yn, duration);
|
||||
}
|
||||
@@ -109,13 +111,12 @@ void Transform::setLonLatZoom(const double lon, const double lat, const double z
|
||||
double new_scale = std::pow(2.0, zoom);
|
||||
|
||||
const double s = new_scale * util::tileSize;
|
||||
zc = s / 2;
|
||||
Bc = s / 360;
|
||||
Cc = s / (2 * M_PI);
|
||||
|
||||
const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999);
|
||||
double xn = -std::round(lon * Bc);
|
||||
double yn = std::round(0.5 * Cc * log((1 + f) / (1 - f)));
|
||||
double xn = -lon * Bc;
|
||||
double yn = 0.5 * Cc * log((1 + f) / (1 - f));
|
||||
|
||||
_setScaleXY(new_scale, xn, yn, duration);
|
||||
}
|
||||
@@ -307,7 +308,6 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
|
||||
}
|
||||
|
||||
const double s = final.scale * util::tileSize;
|
||||
zc = s / 2;
|
||||
Bc = s / 360;
|
||||
Cc = s / (2 * M_PI);
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
VectorTileData::VectorTileData(Tile::ID id, Map &map, const std::string url)
|
||||
: TileData(id, map, url) {
|
||||
VectorTileData::VectorTileData(Tile::ID id, Map &map, const SourceInfo &source)
|
||||
: TileData(id, map, source),
|
||||
depth(id.z >= source.max_zoom ? map.getMaxZoom() - id.z : 1) {
|
||||
}
|
||||
|
||||
VectorTileData::~VectorTileData() {
|
||||
@@ -18,7 +21,8 @@ VectorTileData::~VectorTileData() {
|
||||
}
|
||||
|
||||
void VectorTileData::beforeParse() {
|
||||
parser = std::make_unique<TileParser>(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas());
|
||||
|
||||
parser = std::make_unique<TileParser>(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas(), map.getSprite());
|
||||
}
|
||||
|
||||
void VectorTileData::parse() {
|
||||
@@ -63,7 +67,7 @@ bool VectorTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
|
||||
auto databucket_it = buckets.find(layer_desc->bucket->name);
|
||||
if (databucket_it != buckets.end()) {
|
||||
assert(databucket_it->second);
|
||||
return databucket_it->second->hasData();
|
||||
return databucket_it->second->hasData();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -14,7 +14,7 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer)
|
||||
: fontBuffer(fontBuffer) {
|
||||
}
|
||||
|
||||
void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) {
|
||||
void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/) {
|
||||
painter.renderDebugText(*this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#include <mbgl/renderer/icon_bucket.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/geometry.hpp>
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
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 = "<circle>";
|
||||
}
|
||||
|
||||
const Rect<uint16_t> 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<StyleLayer> 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));
|
||||
}
|
||||
@@ -80,7 +80,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
CapType beginCap = properties.cap;
|
||||
CapType endCap = closed ? CapType::Butt : properties.cap;
|
||||
|
||||
JoinType currentJoin = JoinType::None;
|
||||
JoinType currentJoin = JoinType::Miter;
|
||||
|
||||
Coordinate currentVertex = Coordinate::null(),
|
||||
prevVertex = Coordinate::null(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -205,21 +206,3 @@ const mat4 &Painter::translatedMatrix(const std::array<float, 2> &translation, c
|
||||
return vtxMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::renderMatte() {
|
||||
gl::group group("matte");
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glStencilFunc(GL_EQUAL, 0x0, 0xFF);
|
||||
|
||||
Color matte = {{ 0, 0, 0, 1 }};
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(nativeMatrix);
|
||||
|
||||
// Draw the clipping mask
|
||||
matteArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
plainShader->setColor(matte);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/renderer/fill_bucket.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
@@ -70,10 +71,10 @@ void Painter::renderFill(FillBucket& bucket, const FillProperties& properties, c
|
||||
}
|
||||
|
||||
if ((fill_color[3] >= 1.0f) == (pass == Opaque)) {
|
||||
const std::shared_ptr<Sprite> &sprite = map.getStyle()->sprite;
|
||||
Sprite &sprite = *map.getSprite();
|
||||
if (properties.image.size() && sprite) {
|
||||
SpriteAtlas &spriteAtlas = *map.getSpriteAtlas();
|
||||
Rect<uint16_t> imagePos = spriteAtlas.getImage(properties.image, *sprite);
|
||||
Rect<uint16_t> imagePos = spriteAtlas.getImage(properties.image, sprite);
|
||||
|
||||
|
||||
float factor = 8.0 / std::pow(2, map.getState().getIntegerZoom() - id.z);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/renderer/fill_bucket.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/util/clip_ids.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/renderer/icon_bucket.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
void Painter::renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
|
||||
// Abort early.
|
||||
if (!bucket.hasData()) return;
|
||||
if (pass == Opaque) return;
|
||||
|
||||
const IconProperties &properties = layer_desc->getProperties<IconProperties>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -14,11 +14,13 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d
|
||||
|
||||
float width = properties.width;
|
||||
float offset = properties.offset / 2;
|
||||
float antialiasing = 1 / map.getState().getPixelRatio();
|
||||
float blur = properties.blur + antialiasing;
|
||||
|
||||
// These are the radii of the line. We are limiting it to 16, which will result
|
||||
// in a point size of 64 on retina.
|
||||
float inset = std::fmin((std::fmax(-1, offset - width / 2 - 0.5) + 1), 16.0f);
|
||||
float outset = std::fmin(offset + width / 2 + 0.5, 16.0f);
|
||||
float inset = std::fmin((std::fmax(-1, offset - width / 2 - antialiasing / 2) + 1), 16.0f);
|
||||
float outset = std::fmin(offset + width / 2 + antialiasing / 2, 16.0f);
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
@@ -80,6 +82,7 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d
|
||||
lineShader->setDashArray({{ dash_length, dash_gap }});
|
||||
lineShader->setLineWidth({{ outset, inset }});
|
||||
lineShader->setRatio(map.getState().getPixelRatio());
|
||||
lineShader->setBlur(blur);
|
||||
lineShader->setColor(color);
|
||||
bucket.drawLines(*lineShader);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ void Painter::preparePrerender(PrerenderedTexture &texture) {
|
||||
glViewport(0, 0, texture.properties.size, texture.properties.size);
|
||||
}
|
||||
|
||||
void Painter::finishPrerender(PrerenderedTexture &texture) {
|
||||
void Painter::finishPrerender(PrerenderedTexture &/*texture*/) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) {
|
||||
void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/) {
|
||||
if (pass == Translucent) return;
|
||||
|
||||
const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
|
||||
// const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
|
||||
|
||||
depthMask(false);
|
||||
|
||||
|
||||
217
src/renderer/painter_symbol.cpp
Normal file
217
src/renderer/painter_symbol.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/renderer/symbol_bucket.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> layer_desc,
|
||||
const Tile::ID &/*id*/) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SymbolProperties &properties = layer_desc->getProperties<SymbolProperties>();
|
||||
|
||||
|
||||
if (bucket.hasTextData()) {
|
||||
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);
|
||||
|
||||
useProgram(textShader->program);
|
||||
textShader->setMatrix(matrix);
|
||||
textShader->setExtrudeMatrix(exMatrix);
|
||||
|
||||
GlyphAtlas &glyphAtlas = *map.getGlyphAtlas();
|
||||
glyphAtlas.bind();
|
||||
textShader->setTextureSize(
|
||||
{{static_cast<float>(glyphAtlas.width), static_cast<float>(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<FrameSnapshot> &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 = 1.0f / 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()) {
|
||||
mat4 exMatrix;
|
||||
matrix::copy(exMatrix, projMatrix);
|
||||
|
||||
const float angleOffset =
|
||||
bucket.properties.icon.rotation_alignment == RotationAlignmentType::Map
|
||||
? map.getState().getAngle()
|
||||
: 0;
|
||||
|
||||
if (angleOffset) {
|
||||
matrix::rotate_z(exMatrix, exMatrix, angleOffset);
|
||||
}
|
||||
|
||||
// If layerStyle.size > bucket.info.fontSize then labels may collide
|
||||
const float fontSize = properties.icon.size != 0 ? properties.icon.size : bucket.properties.icon.max_size;
|
||||
const float fontScale = fontSize / 1.0f;
|
||||
matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f);
|
||||
|
||||
useProgram(iconShader->program);
|
||||
iconShader->setMatrix(matrix);
|
||||
iconShader->setExtrudeMatrix(exMatrix);
|
||||
|
||||
SpriteAtlas &spriteAtlas = *map.getSpriteAtlas();
|
||||
spriteAtlas.bind(map.getState().isChanging() || bucket.properties.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1);
|
||||
iconShader->setTextureSize(
|
||||
{{static_cast<float>(spriteAtlas.getWidth()), static_cast<float>(spriteAtlas.getHeight())}});
|
||||
|
||||
// Convert the -pi..pi to an int8 range.
|
||||
const float angle = std::round((map.getState().getAngle()) / M_PI * 128);
|
||||
|
||||
// adjust min/max zooms for variable font sies
|
||||
float zoomAdjust = log(fontSize / bucket.properties.icon.max_size) / log(2);
|
||||
|
||||
iconShader->setAngle((int32_t)(angle + 256) % 256);
|
||||
iconShader->setFlip(bucket.properties.placement == PlacementType::Line ? 1 : 0);
|
||||
iconShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) *
|
||||
10); // current zoom level
|
||||
|
||||
iconShader->setFadeDist(0 * 10);
|
||||
iconShader->setMinFadeZoom(map.getState().getNormalizedZoom() * 10);
|
||||
iconShader->setMaxFadeZoom(map.getState().getNormalizedZoom() * 10);
|
||||
iconShader->setFadeZoom(map.getState().getNormalizedZoom() * 10);
|
||||
iconShader->setOpacity(properties.icon.opacity);
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawIcons(*iconShader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/renderer/text_bucket.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <cmath>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
void Painter::renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) return;
|
||||
if (!bucket.hasData()) return;
|
||||
|
||||
const TextProperties &properties = layer_desc->getProperties<TextProperties>();
|
||||
|
||||
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<float>(glyphAtlas.width),
|
||||
static_cast<float>(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<FrameSnapshot> &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);
|
||||
}
|
||||
}
|
||||
394
src/renderer/symbol_bucket.cpp
Normal file
394
src/renderer/symbol_bucket.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
#include <mbgl/renderer/symbol_bucket.hpp>
|
||||
#include <mbgl/geometry/text_buffer.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/geometry/geometry.hpp>
|
||||
#include <mbgl/geometry/anchor.hpp>
|
||||
#include <mbgl/geometry/interpolate.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/text/collision.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
|
||||
#include <mbgl/util/utf.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
SymbolBucket::SymbolBucket(const StyleBucketSymbol &properties, Collision &collision)
|
||||
: properties(properties), collision(collision) {}
|
||||
|
||||
void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
|
||||
const Tile::ID &id) {
|
||||
painter.renderSymbol(*this, layer_desc, id);
|
||||
}
|
||||
|
||||
bool SymbolBucket::hasData() const { return hasTextData() || hasIconData(); }
|
||||
|
||||
bool SymbolBucket::hasTextData() const { return !text.groups.empty(); }
|
||||
|
||||
bool SymbolBucket::hasIconData() const { return !icon.groups.empty(); }
|
||||
|
||||
void SymbolBucket::addGlyphsToAtlas(uint64_t tileid, const std::string stackname,
|
||||
const std::u32string &string, const FontStack &fontStack,
|
||||
GlyphAtlas &glyphAtlas, GlyphPositions &face) {
|
||||
const std::map<uint32_t, SDFGlyph> &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<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf);
|
||||
face.emplace(chr, Glyph{rect, sdf.metrics});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer,
|
||||
const FilterExpression &filter,
|
||||
GlyphStore &glyphStore,
|
||||
const Sprite &sprite) {
|
||||
const bool text = properties.text.field.size();
|
||||
const bool icon = properties.icon.image.size();
|
||||
|
||||
std::vector<SymbolFeature> features;
|
||||
|
||||
if (!text && !icon) {
|
||||
return features;
|
||||
}
|
||||
|
||||
util::utf8_to_utf32 ucs4conv;
|
||||
|
||||
// Determine and load glyph ranges
|
||||
std::set<GlyphRange> ranges;
|
||||
|
||||
FilteredVectorTileLayer filtered_layer(layer, filter);
|
||||
for (const pbf &feature_pbf : filtered_layer) {
|
||||
const VectorTileFeature feature{feature_pbf, layer};
|
||||
|
||||
SymbolFeature ft;
|
||||
|
||||
if (text) {
|
||||
std::string u8string = util::replaceTokens(properties.text.field, feature.properties);
|
||||
|
||||
auto &convert = std::use_facet<std::ctype<char>>(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());
|
||||
}
|
||||
|
||||
ft.label = ucs4conv.convert(u8string);
|
||||
|
||||
if (ft.label.size()) {
|
||||
// Loop through all characters of this text and collect unique codepoints.
|
||||
for (char32_t chr : ft.label) {
|
||||
ranges.insert(getGlyphRange(chr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (icon) {
|
||||
ft.sprite = util::replaceTokens(properties.icon.image, feature.properties);
|
||||
}
|
||||
|
||||
if (ft.label.length() || ft.sprite.length()) {
|
||||
ft.geometry = feature.geometry;
|
||||
features.push_back(std::move(ft));
|
||||
}
|
||||
}
|
||||
|
||||
glyphStore.waitForGlyphRanges(properties.text.font, ranges);
|
||||
sprite.waitUntilLoaded();
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
|
||||
const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite,
|
||||
GlyphAtlas &glyphAtlas, GlyphStore &glyphStore) {
|
||||
|
||||
const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite);
|
||||
|
||||
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;
|
||||
|
||||
float justify = 0.5;
|
||||
if (properties.text.justify == TextJustifyType::Right) justify = 1;
|
||||
else if (properties.text.justify == TextJustifyType::Left) justify = 0;
|
||||
|
||||
const FontStack &fontStack = glyphStore.getFontStack(properties.text.font);
|
||||
|
||||
for (const SymbolFeature &feature : features) {
|
||||
Shaping shaping;
|
||||
Rect<uint16_t> image;
|
||||
GlyphPositions face;
|
||||
|
||||
// if feature has text, shape the text
|
||||
if (feature.label.length()) {
|
||||
shaping = fontStack.getShaping(
|
||||
/* string */ feature.label,
|
||||
/* maxWidth */ properties.text.max_width,
|
||||
/* lineHeight */ properties.text.line_height,
|
||||
/* horizontalAlign */ horizontalAlign,
|
||||
/* verticalAlign */ verticalAlign,
|
||||
/* justify */ justify,
|
||||
/* spacing */ properties.text.letter_spacing,
|
||||
/* translate */ properties.text.offset);
|
||||
|
||||
// Add the glyphs we need for this label to the glyph atlas.
|
||||
if (shaping.size()) {
|
||||
addGlyphsToAtlas(id.to_uint64(), properties.text.font, feature.label, fontStack,
|
||||
glyphAtlas, face);
|
||||
}
|
||||
}
|
||||
|
||||
// if feature has icon, get sprite atlas position
|
||||
if (feature.sprite.length()) {
|
||||
image = spriteAtlas.waitForImage(feature.sprite, sprite);
|
||||
}
|
||||
|
||||
// if either shaping or icon position is present, add the feature
|
||||
if (shaping.size() || image) {
|
||||
addFeature(feature.geometry, shaping, face, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping,
|
||||
const GlyphPositions &face, const Rect<uint16_t> &image) {
|
||||
// Decode all lines.
|
||||
std::vector<Coordinate> 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()) {
|
||||
addFeature(line, shaping, face, image);
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
line.emplace_back(x, y);
|
||||
}
|
||||
if (line.size()) {
|
||||
addFeature(line, shaping, face, image);
|
||||
}
|
||||
}
|
||||
|
||||
bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; }
|
||||
|
||||
const PlacementRange fullRange{{2 * M_PI, 0}};
|
||||
|
||||
void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping &shaping,
|
||||
const GlyphPositions &face, const Rect<uint16_t> &image) {
|
||||
assert(line.size());
|
||||
|
||||
const float minScale = 0.5f;
|
||||
const float glyphSize = 24.0f;
|
||||
|
||||
const bool horizontalText =
|
||||
properties.text.rotation_alignment == RotationAlignmentType::Viewport;
|
||||
const bool horizontalIcon =
|
||||
properties.icon.rotation_alignment == RotationAlignmentType::Viewport;
|
||||
const float fontScale = properties.text.max_size / glyphSize;
|
||||
const float textBoxScale = collision.tilePixelRatio * fontScale;
|
||||
const float iconBoxScale = collision.tilePixelRatio * properties.icon.max_size;
|
||||
const bool iconWithoutText = properties.text.optional || !shaping.size();
|
||||
const bool textWithoutIcon = properties.icon.optional || !image;
|
||||
|
||||
Anchors anchors;
|
||||
|
||||
if (properties.placement == PlacementType::Line) {
|
||||
// Line labels
|
||||
anchors = interpolate(line, properties.min_distance, minScale, collision.maxPlacementScale,
|
||||
collision.tilePixelRatio);
|
||||
|
||||
// Sort anchors by segment so that we can start placement with the
|
||||
// anchors that can be shown at the lowest zoom levels.
|
||||
std::sort(anchors.begin(), anchors.end(), byScale);
|
||||
|
||||
} else {
|
||||
// Point labels
|
||||
anchors = {Anchor{float(line[0].x), float(line[0].y), 0, minScale}};
|
||||
}
|
||||
|
||||
// TODO: figure out correct ascender height.
|
||||
const vec2<float> origin = {0, -17};
|
||||
|
||||
for (Anchor &anchor : anchors) {
|
||||
|
||||
// Calculate the scales at which the text and icons can be first shown without overlap
|
||||
Placement glyphPlacement;
|
||||
Placement iconPlacement;
|
||||
float glyphScale = 0;
|
||||
float iconScale = 0;
|
||||
|
||||
if (shaping.size()) {
|
||||
glyphPlacement = Placement::getGlyphs(anchor, origin, shaping, face, textBoxScale,
|
||||
horizontalText, line, properties);
|
||||
glyphScale =
|
||||
properties.text.allow_overlap
|
||||
? glyphPlacement.minScale
|
||||
: collision.getPlacementScale(glyphPlacement.boxes, glyphPlacement.minScale);
|
||||
if (!glyphScale && !iconWithoutText)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
iconPlacement = Placement::getIcon(anchor, image, iconBoxScale, line, properties);
|
||||
iconScale =
|
||||
properties.icon.allow_overlap
|
||||
? iconPlacement.minScale
|
||||
: collision.getPlacementScale(iconPlacement.boxes, iconPlacement.minScale);
|
||||
if (!iconScale && !textWithoutIcon)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!iconWithoutText && !textWithoutIcon) {
|
||||
iconScale = glyphScale = util::max(iconScale, glyphScale);
|
||||
} else if (!textWithoutIcon && glyphScale) {
|
||||
glyphScale = util::max(iconScale, glyphScale);
|
||||
} else if (!iconWithoutText && iconScale) {
|
||||
iconScale = util::max(iconScale, glyphScale);
|
||||
}
|
||||
|
||||
// Get the rotation ranges it is safe to show the glyphs
|
||||
PlacementRange glyphRange =
|
||||
(!glyphScale || properties.text.allow_overlap)
|
||||
? fullRange
|
||||
: collision.getPlacementRange(glyphPlacement.boxes, glyphScale, horizontalText);
|
||||
PlacementRange iconRange =
|
||||
(!iconScale || properties.icon.allow_overlap)
|
||||
? fullRange
|
||||
: collision.getPlacementRange(iconPlacement.boxes, iconScale, horizontalIcon);
|
||||
|
||||
const PlacementRange maxRange = {{
|
||||
util::min(iconRange[0], glyphRange[0]), util::max(iconRange[1], glyphRange[1]),
|
||||
}};
|
||||
|
||||
if (!iconWithoutText && !textWithoutIcon) {
|
||||
iconRange = glyphRange = maxRange;
|
||||
} else if (!textWithoutIcon) {
|
||||
glyphRange = maxRange;
|
||||
} else if (!iconWithoutText) {
|
||||
iconRange = maxRange;
|
||||
}
|
||||
|
||||
// Insert final placement into collision tree and add glyphs/icons to buffers
|
||||
if (glyphScale) {
|
||||
if (!properties.text.ignore_placement) {
|
||||
collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange,
|
||||
horizontalText);
|
||||
}
|
||||
addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange);
|
||||
}
|
||||
|
||||
if (iconScale) {
|
||||
if (!properties.icon.ignore_placement) {
|
||||
collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon);
|
||||
}
|
||||
addSymbols(icon, iconPlacement.shapes, iconScale, iconRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale,
|
||||
PlacementRange placementRange) {
|
||||
const float zoom = collision.zoom;
|
||||
|
||||
const float placementZoom = std::log(scale) / std::log(2) + zoom;
|
||||
|
||||
for (const PlacedGlyph &symbol : symbols) {
|
||||
const auto &tl = symbol.tl;
|
||||
const auto &tr = symbol.tr;
|
||||
const auto &bl = symbol.bl;
|
||||
const auto &br = symbol.br;
|
||||
const auto &tex = symbol.tex;
|
||||
const auto &angle = symbol.angle;
|
||||
|
||||
float minZoom =
|
||||
util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom);
|
||||
float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
|
||||
const auto &glyphAnchor = symbol.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 (!buffer.groups.size() ||
|
||||
(buffer.groups.back().vertex_length + glyph_vertex_length > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
buffer.groups.emplace_back();
|
||||
}
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
// coordinate in this polygon.
|
||||
ElementGroup &triangleGroup = buffer.groups.back();
|
||||
uint32_t triangleIndex = triangleGroup.vertex_length;
|
||||
|
||||
// coordinates (2 triangles)
|
||||
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom,
|
||||
placementRange, maxZoom, placementZoom);
|
||||
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle,
|
||||
minZoom, placementRange, maxZoom, placementZoom);
|
||||
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle,
|
||||
minZoom, placementRange, maxZoom, placementZoom);
|
||||
buffer.vertices.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.
|
||||
buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
|
||||
buffer.triangles.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(0);
|
||||
char *elements_index = BUFFER_OFFSET(0);
|
||||
for (ElementGroup &group : text.groups) {
|
||||
group.array.bind(shader, text.vertices, text.triangles, vertex_index);
|
||||
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
|
||||
vertex_index += group.vertex_length * text.vertices.itemSize;
|
||||
elements_index += group.elements_length * text.triangles.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolBucket::drawIcons(IconShader &shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(0);
|
||||
char *elements_index = BUFFER_OFFSET(0);
|
||||
for (ElementGroup &group : icon.groups) {
|
||||
group.array.bind(shader, icon.vertices, icon.triangles, vertex_index);
|
||||
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
|
||||
vertex_index += group.vertex_length * icon.vertices.itemSize;
|
||||
elements_index += group.elements_length * icon.triangles.itemSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
#include <mbgl/renderer/text_bucket.hpp>
|
||||
#include <mbgl/geometry/text_buffer.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/geometry.hpp>
|
||||
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
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<float>(zoom + log(glyph.glyphBox.minScale) / log(2)),
|
||||
placementZoom);
|
||||
float maxZoom = util::min(
|
||||
static_cast<float>(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<Coordinate> 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<StyleLayer> 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;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
uniform sampler2D u_image;
|
||||
uniform vec2 u_dimension;
|
||||
uniform vec4 u_color;
|
||||
uniform float u_size;
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
varying vec2 v_tex;
|
||||
varying float v_alpha;
|
||||
|
||||
void main() {
|
||||
vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension;
|
||||
gl_FragColor = u_color * texture2D(u_image, pos);
|
||||
// Note: We don't need to premultiply here as the image we use is already
|
||||
// premultiplied in the sprite atlas.
|
||||
gl_FragColor = texture2D(u_texture, v_tex) * v_alpha;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,73 @@
|
||||
attribute vec2 a_pos;
|
||||
attribute vec2 a_offset;
|
||||
attribute vec2 a_tex;
|
||||
attribute float a_angle;
|
||||
attribute float a_minzoom;
|
||||
attribute float a_maxzoom;
|
||||
attribute float a_rangeend;
|
||||
attribute float a_rangestart;
|
||||
attribute float a_labelminzoom;
|
||||
|
||||
|
||||
// posmatrix is for the vertex position, exmatrix is for rotating and projecting
|
||||
// the extrusion vector.
|
||||
uniform mat4 u_matrix;
|
||||
uniform float u_size;
|
||||
uniform float u_ratio;
|
||||
uniform mat4 u_exmatrix;
|
||||
uniform float u_angle;
|
||||
uniform float u_zoom;
|
||||
uniform float u_flip;
|
||||
uniform float u_fadedist;
|
||||
uniform float u_minfadezoom;
|
||||
uniform float u_maxfadezoom;
|
||||
uniform float u_fadezoom;
|
||||
uniform float u_opacity;
|
||||
|
||||
uniform vec2 u_texsize;
|
||||
|
||||
varying vec2 v_tex;
|
||||
varying float v_alpha;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_matrix * vec4(a_pos, 0, 1);
|
||||
gl_PointSize = u_size;
|
||||
v_tex = a_tex * u_ratio;
|
||||
|
||||
float a_fadedist = 10.0;
|
||||
float rev = 0.0;
|
||||
|
||||
// u_angle is angle of the map, -128..128 representing 0..2PI
|
||||
// a_angle is angle of the label, 0..256 representing 0..2PI, where 0 is horizontal text
|
||||
float rotated = mod(a_angle + u_angle, 256.0);
|
||||
// if the label rotates with the map, and if the rotated label is upside down, hide it
|
||||
if (u_flip > 0.0 && rotated >= 64.0 && rotated < 192.0) rev = 1.0;
|
||||
|
||||
// If the label should be invisible, we move the vertex outside
|
||||
// of the view plane so that the triangle gets clipped. This makes it easier
|
||||
// for us to create degenerate triangle strips.
|
||||
// u_zoom is the current zoom level adjusted for the change in font size
|
||||
float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)) + rev;
|
||||
|
||||
// fade out labels
|
||||
float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);
|
||||
|
||||
if (u_fadedist >= 0.0) {
|
||||
v_alpha = alpha;
|
||||
} else {
|
||||
v_alpha = 1.0 - alpha;
|
||||
}
|
||||
if (u_maxfadezoom < a_labelminzoom) {
|
||||
v_alpha = 0.0;
|
||||
}
|
||||
if (u_minfadezoom >= a_labelminzoom) {
|
||||
v_alpha = 1.0;
|
||||
}
|
||||
|
||||
// if label has been faded out, clip it
|
||||
z += step(v_alpha, 0.0);
|
||||
|
||||
// all the angles are 0..256 representing 0..2PI
|
||||
// hide if (angle >= a_rangeend && angle < rangestart)
|
||||
z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle));
|
||||
|
||||
gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0);
|
||||
v_tex = a_tex / u_texsize;
|
||||
|
||||
v_alpha *= u_opacity;
|
||||
}
|
||||
|
||||
@@ -17,62 +17,138 @@ IconShader::IconShader()
|
||||
}
|
||||
|
||||
a_pos = glGetAttribLocation(program, "a_pos");
|
||||
a_offset = glGetAttribLocation(program, "a_offset");
|
||||
a_tex = glGetAttribLocation(program, "a_tex");
|
||||
a_angle = glGetAttribLocation(program, "a_angle");
|
||||
a_minzoom = glGetAttribLocation(program, "a_minzoom");
|
||||
a_maxzoom = glGetAttribLocation(program, "a_maxzoom");
|
||||
a_rangeend = glGetAttribLocation(program, "a_rangeend");
|
||||
a_rangestart = glGetAttribLocation(program, "a_rangestart");
|
||||
a_labelminzoom = glGetAttribLocation(program, "a_labelminzoom");
|
||||
|
||||
u_matrix = glGetUniformLocation(program, "u_matrix");
|
||||
u_color = glGetUniformLocation(program, "u_color");
|
||||
u_size = glGetUniformLocation(program, "u_size");
|
||||
u_ratio = glGetUniformLocation(program, "u_ratio");
|
||||
u_dimension = glGetUniformLocation(program, "u_dimension");
|
||||
u_exmatrix = glGetUniformLocation(program, "u_exmatrix");
|
||||
u_angle = glGetUniformLocation(program, "u_angle");
|
||||
u_zoom = glGetUniformLocation(program, "u_zoom");
|
||||
u_flip = glGetUniformLocation(program, "u_flip");
|
||||
u_fadedist = glGetUniformLocation(program, "u_fadedist");
|
||||
u_minfadezoom = glGetUniformLocation(program, "u_minfadezoom");
|
||||
u_maxfadezoom = glGetUniformLocation(program, "u_maxfadezoom");
|
||||
u_fadezoom = glGetUniformLocation(program, "u_fadezoom");
|
||||
u_opacity = glGetUniformLocation(program, "u_opacity");
|
||||
u_texsize = glGetUniformLocation(program, "u_texsize");
|
||||
|
||||
// fprintf(stderr, "IconShader:\n");
|
||||
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
|
||||
// fprintf(stderr, " - u_color: %d\n", u_color);
|
||||
// fprintf(stderr, " - u_size: %d\n", u_size);
|
||||
// fprintf(stderr, " - u_ratio: %d\n", u_ratio);
|
||||
// fprintf(stderr, " - u_dimension: %d\n", u_dimension);
|
||||
// fprintf(stderr, " - u_image: %d\n", u_image);
|
||||
// fprintf(stderr, " - u_exmatrix: %d\n", u_exmatrix);
|
||||
// fprintf(stderr, " - u_angle: %d\n", u_angle);
|
||||
// fprintf(stderr, " - u_zoom: %d\n", u_zoom);
|
||||
// fprintf(stderr, " - u_flip: %d\n", u_flip);
|
||||
// fprintf(stderr, " - u_fadedist: %d\n", u_fadedist);
|
||||
// fprintf(stderr, " - u_minfadezoom: %d\n", u_minfadezoom);
|
||||
// fprintf(stderr, " - u_maxfadezoom: %d\n", u_maxfadezoom);
|
||||
// fprintf(stderr, " - u_fadezoom: %d\n", u_fadezoom);
|
||||
// fprintf(stderr, " - u_opacity: %d\n", u_opacity);
|
||||
}
|
||||
|
||||
void IconShader::bind(char *offset) {
|
||||
const int stride = 20;
|
||||
|
||||
glEnableVertexAttribArray(a_pos);
|
||||
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset);
|
||||
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, stride, offset + 0);
|
||||
|
||||
glEnableVertexAttribArray(a_offset);
|
||||
glVertexAttribPointer(a_offset, 2, GL_SHORT, false, stride, offset + 4);
|
||||
|
||||
glEnableVertexAttribArray(a_labelminzoom);
|
||||
glVertexAttribPointer(a_labelminzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 8);
|
||||
|
||||
glEnableVertexAttribArray(a_minzoom);
|
||||
glVertexAttribPointer(a_minzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 9);
|
||||
|
||||
glEnableVertexAttribArray(a_maxzoom);
|
||||
glVertexAttribPointer(a_maxzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 10);
|
||||
|
||||
glEnableVertexAttribArray(a_angle);
|
||||
glVertexAttribPointer(a_angle, 1, GL_UNSIGNED_BYTE, false, stride, offset + 11);
|
||||
|
||||
glEnableVertexAttribArray(a_rangeend);
|
||||
glVertexAttribPointer(a_rangeend, 1, GL_UNSIGNED_BYTE, false, stride, offset + 12);
|
||||
|
||||
glEnableVertexAttribArray(a_rangestart);
|
||||
glVertexAttribPointer(a_rangestart, 1, GL_UNSIGNED_BYTE, false, stride, offset + 13);
|
||||
|
||||
glEnableVertexAttribArray(a_tex);
|
||||
glVertexAttribPointer(a_tex, 2, GL_UNSIGNED_SHORT, false, 8, offset + 4);
|
||||
glVertexAttribPointer(a_tex, 2, GL_SHORT, false, stride, offset + 16);
|
||||
}
|
||||
|
||||
void IconShader::setImage(int32_t new_image) {
|
||||
if (image != new_image) {
|
||||
glUniform1i(u_image, new_image);
|
||||
image = new_image;
|
||||
void IconShader::setExtrudeMatrix(const std::array<float, 16>& new_exmatrix) {
|
||||
if (exmatrix != new_exmatrix) {
|
||||
glUniformMatrix4fv(u_exmatrix, 1, GL_FALSE, new_exmatrix.data());
|
||||
exmatrix = new_exmatrix;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setColor(const std::array<float, 4>& new_color) {
|
||||
if (color != new_color) {
|
||||
glUniform4fv(u_color, 1, new_color.data());
|
||||
color = new_color;
|
||||
void IconShader::setAngle(float new_angle) {
|
||||
if (angle != new_angle) {
|
||||
glUniform1f(u_angle, new_angle);
|
||||
angle = new_angle;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setSize(float new_size) {
|
||||
if (size != new_size) {
|
||||
glUniform1f(u_size, new_size);
|
||||
size = new_size;
|
||||
void IconShader::setZoom(float new_zoom) {
|
||||
if (zoom != new_zoom) {
|
||||
glUniform1f(u_zoom, new_zoom);
|
||||
zoom = new_zoom;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setRatio(float new_ratio) {
|
||||
if (ratio != new_ratio) {
|
||||
glUniform1f(u_ratio, new_ratio);
|
||||
ratio = new_ratio;
|
||||
void IconShader::setFlip(float new_flip) {
|
||||
if (flip != new_flip) {
|
||||
glUniform1f(u_flip, new_flip);
|
||||
flip = new_flip;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setDimension(const std::array<float, 2>& new_dimension) {
|
||||
if (dimension != new_dimension) {
|
||||
glUniform2fv(u_dimension, 1, new_dimension.data());
|
||||
dimension = new_dimension;
|
||||
void IconShader::setFadeDist(float new_fadedist) {
|
||||
if (fadedist != new_fadedist) {
|
||||
glUniform1f(u_fadedist, new_fadedist);
|
||||
fadedist = new_fadedist;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setMinFadeZoom(float new_minfadezoom) {
|
||||
if (minfadezoom != new_minfadezoom) {
|
||||
glUniform1f(u_minfadezoom, new_minfadezoom);
|
||||
minfadezoom = new_minfadezoom;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setMaxFadeZoom(float new_maxfadezoom) {
|
||||
if (maxfadezoom != new_maxfadezoom) {
|
||||
glUniform1f(u_maxfadezoom, new_maxfadezoom);
|
||||
maxfadezoom = new_maxfadezoom;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setFadeZoom(float new_fadezoom) {
|
||||
if (fadezoom != new_fadezoom) {
|
||||
glUniform1f(u_fadezoom, new_fadezoom);
|
||||
fadezoom = new_fadezoom;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setOpacity(float new_opacity) {
|
||||
if (opacity != new_opacity) {
|
||||
glUniform1f(u_opacity, new_opacity);
|
||||
opacity = new_opacity;
|
||||
}
|
||||
}
|
||||
|
||||
void IconShader::setTextureSize(const std::array<float, 2> &new_texsize) {
|
||||
if (texsize != new_texsize) {
|
||||
glUniform2fv(u_texsize, 1, new_texsize.data());
|
||||
texsize = new_texsize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
uniform vec2 u_linewidth;
|
||||
uniform vec4 u_color;
|
||||
uniform float u_blur;
|
||||
|
||||
uniform vec2 u_dasharray;
|
||||
|
||||
@@ -13,7 +14,7 @@ void main() {
|
||||
// Calculate the antialiasing fade factor. This is either when fading in
|
||||
// the line in case of an offset line (v_linewidth.t) or when fading out
|
||||
// (v_linewidth.s)
|
||||
float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);
|
||||
float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0);
|
||||
|
||||
// Calculate the antialiasing fade factor based on distance to the dash.
|
||||
// Only affects alpha when line is dashed
|
||||
@@ -21,4 +22,4 @@ void main() {
|
||||
alpha *= max(step(0.0, -u_dasharray.y), clamp(min(pos, u_dasharray.x - pos), 0.0, 1.0));
|
||||
|
||||
gl_FragColor = u_color * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ LineShader::LineShader()
|
||||
u_color = glGetUniformLocation(program, "u_color");
|
||||
u_ratio = glGetUniformLocation(program, "u_ratio");
|
||||
u_dasharray = glGetUniformLocation(program, "u_dasharray");
|
||||
u_blur = glGetUniformLocation(program, "u_blur");
|
||||
|
||||
// fprintf(stderr, "LineShader:\n");
|
||||
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
|
||||
@@ -81,3 +82,10 @@ void LineShader::setDashArray(const std::array<float, 2>& new_dasharray) {
|
||||
dasharray = new_dasharray;
|
||||
}
|
||||
}
|
||||
|
||||
void LineShader::setBlur(float new_blur) {
|
||||
if (blur != new_blur) {
|
||||
glUniform1f(u_blur, new_blur);
|
||||
blur = new_blur;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ TextShader::TextShader()
|
||||
// fprintf(stderr, " - u_maxfadezoom: %d\n", u_maxfadezoom);
|
||||
// fprintf(stderr, " - u_fadezoom: %d\n", u_fadezoom);
|
||||
// fprintf(stderr, " - u_texsize: %d\n", u_texsize);
|
||||
// fprintf(stderr, " - u_opacity: %d\n", u_opacity);
|
||||
}
|
||||
|
||||
void TextShader::bind(char *offset) {
|
||||
|
||||
@@ -1,48 +1,60 @@
|
||||
#include <mbgl/style/property_fallback.hpp>
|
||||
#include <mbgl/style/style_properties.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
|
||||
{ PropertyKey::FillAntialias, true },
|
||||
{ PropertyKey::FillOpacity, 1.0f },
|
||||
{ PropertyKey::FillColor, Color({{ 0, 0, 0, 1 }}) },
|
||||
{ PropertyKey::FillTranslateX, 0.0f },
|
||||
{ PropertyKey::FillTranslateY, 0.0f },
|
||||
{ PropertyKey::FillTranslateAnchor, TranslateAnchorType::Map },
|
||||
{ PropertyKey::FillAntialias, defaultStyleProperties<FillProperties>().antialias },
|
||||
{ PropertyKey::FillOpacity, defaultStyleProperties<FillProperties>().opacity },
|
||||
{ PropertyKey::FillColor, defaultStyleProperties<FillProperties>().fill_color },
|
||||
// no FillOutlineColor on purpose.
|
||||
{ PropertyKey::FillTranslateX, defaultStyleProperties<FillProperties>().translate[0] },
|
||||
{ PropertyKey::FillTranslateY, defaultStyleProperties<FillProperties>().translate[1] },
|
||||
{ PropertyKey::FillTranslateAnchor, defaultStyleProperties<FillProperties>().translateAnchor },
|
||||
|
||||
{ PropertyKey::LineOpacity, 1.0f },
|
||||
{ PropertyKey::LineColor, Color({{ 0, 0, 0, 1 }}) },
|
||||
{ PropertyKey::LineTranslateX, 0.0f },
|
||||
{ PropertyKey::LineTranslateY, 0.0f },
|
||||
{ PropertyKey::LineTranslateAnchor, TranslateAnchorType::Map },
|
||||
{ PropertyKey::LineWidth, 1.0f },
|
||||
{ PropertyKey::LineOffset, 0.0f },
|
||||
{ PropertyKey::LineBlur, 1.0f },
|
||||
{ PropertyKey::LineDashLand, 1.0f },
|
||||
{ PropertyKey::LineDashGap, -1.0f },
|
||||
{ PropertyKey::LineOpacity, defaultStyleProperties<LineProperties>().opacity },
|
||||
{ PropertyKey::LineColor, defaultStyleProperties<LineProperties>().color },
|
||||
{ PropertyKey::LineTranslateX, defaultStyleProperties<LineProperties>().translate[0] },
|
||||
{ PropertyKey::LineTranslateY, defaultStyleProperties<LineProperties>().translate[1] },
|
||||
{ PropertyKey::LineTranslateAnchor, defaultStyleProperties<LineProperties>().translateAnchor },
|
||||
{ PropertyKey::LineWidth, defaultStyleProperties<LineProperties>().width },
|
||||
{ PropertyKey::LineOffset, defaultStyleProperties<LineProperties>().offset },
|
||||
{ PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur },
|
||||
{ PropertyKey::LineDashLand, defaultStyleProperties<LineProperties>().dash_array[0] },
|
||||
{ PropertyKey::LineDashGap, defaultStyleProperties<LineProperties>().dash_array[1] },
|
||||
|
||||
{ PropertyKey::IconOpacity, 1.0f },
|
||||
{ PropertyKey::IconRotate, 0.0f },
|
||||
{ PropertyKey::IconRotateAnchor, RotateAnchorType::Viewport },
|
||||
{ PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity },
|
||||
{ PropertyKey::IconRotate, defaultStyleProperties<SymbolProperties>().icon.rotate },
|
||||
{ PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size },
|
||||
{ PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color },
|
||||
{ PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color },
|
||||
{ PropertyKey::IconHaloWidth, defaultStyleProperties<SymbolProperties>().icon.halo_width },
|
||||
{ PropertyKey::IconHaloBlur, defaultStyleProperties<SymbolProperties>().icon.halo_blur },
|
||||
{ PropertyKey::IconTranslateX, defaultStyleProperties<SymbolProperties>().icon.translate[0] },
|
||||
{ PropertyKey::IconTranslateY, defaultStyleProperties<SymbolProperties>().icon.translate[1] },
|
||||
{ PropertyKey::IconTranslateAnchor, defaultStyleProperties<SymbolProperties>().icon.translate_anchor },
|
||||
|
||||
{ PropertyKey::TextOpacity, 1.0f },
|
||||
{ PropertyKey::TextSize, 16.0f },
|
||||
{ PropertyKey::TextColor, Color({{ 0, 0, 0, 1 }}) },
|
||||
{ PropertyKey::TextHaloColor, Color({{ 0, 0, 0, 0 }}) },
|
||||
{ PropertyKey::TextHaloWidth, 0.25f },
|
||||
{ PropertyKey::TextHaloBlur, 1.0f },
|
||||
{ PropertyKey::TextOpacity, defaultStyleProperties<SymbolProperties>().text.opacity },
|
||||
{ PropertyKey::TextSize, defaultStyleProperties<SymbolProperties>().text.size },
|
||||
{ PropertyKey::TextColor, defaultStyleProperties<SymbolProperties>().text.color },
|
||||
{ PropertyKey::TextHaloColor, defaultStyleProperties<SymbolProperties>().text.halo_color },
|
||||
{ PropertyKey::TextHaloWidth, defaultStyleProperties<SymbolProperties>().text.halo_width },
|
||||
{ PropertyKey::TextHaloBlur, defaultStyleProperties<SymbolProperties>().text.halo_blur },
|
||||
{ PropertyKey::TextTranslateX, defaultStyleProperties<SymbolProperties>().text.translate[0] },
|
||||
{ PropertyKey::TextTranslateY, defaultStyleProperties<SymbolProperties>().text.translate[1] },
|
||||
{ PropertyKey::TextTranslateAnchor, defaultStyleProperties<SymbolProperties>().text.translate_anchor },
|
||||
|
||||
{ PropertyKey::CompositeOpacity, 1.0f },
|
||||
{ PropertyKey::CompositeOpacity, defaultStyleProperties<CompositeProperties>().opacity },
|
||||
|
||||
{ PropertyKey::RasterOpacity, 1.0f },
|
||||
{ PropertyKey::RasterSpin, 0.0f },
|
||||
{ PropertyKey::RasterBrightnessLow, 0.0f },
|
||||
{ PropertyKey::RasterBrightnessHigh, 1.0f },
|
||||
{ PropertyKey::RasterSaturation, 0.0f },
|
||||
{ PropertyKey::RasterContrast, 0.0f },
|
||||
{ PropertyKey::RasterFade, 0.0f },
|
||||
{ PropertyKey::RasterOpacity, defaultStyleProperties<RasterProperties>().opacity },
|
||||
{ PropertyKey::RasterSpin, defaultStyleProperties<RasterProperties>().spin },
|
||||
{ PropertyKey::RasterBrightnessLow, defaultStyleProperties<RasterProperties>().brightness[0] },
|
||||
{ PropertyKey::RasterBrightnessHigh, defaultStyleProperties<RasterProperties>().brightness[1] },
|
||||
{ PropertyKey::RasterSaturation, defaultStyleProperties<RasterProperties>().saturation },
|
||||
{ PropertyKey::RasterContrast, defaultStyleProperties<RasterProperties>().contrast },
|
||||
{ PropertyKey::RasterFade, defaultStyleProperties<RasterProperties>().fade },
|
||||
|
||||
{ PropertyKey::BackgroundColor, Color({{ 0, 0, 0, 0 }}) },
|
||||
{ PropertyKey::BackgroundColor, defaultStyleProperties<BackgroundProperties>().color },
|
||||
};
|
||||
|
||||
const PropertyValue PropertyFallbackValue::defaultProperty = false;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/style/style_layer_group.hpp>
|
||||
#include <mbgl/style/style_parser.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
@@ -29,6 +30,10 @@ void Style::updateProperties(float z, timestamp now) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &Style::getSpriteURL() const {
|
||||
return sprite_url;
|
||||
}
|
||||
|
||||
void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) {
|
||||
defaultTransition.duration = duration_milliseconds;
|
||||
}
|
||||
@@ -93,7 +98,7 @@ void Style::loadJSON(const uint8_t *const data) {
|
||||
const BackgroundProperties &Style::getBackgroundProperties() const {
|
||||
if (layers && layers->layers.size()) {
|
||||
const auto first = layers->layers.front();
|
||||
if (first && first->id == "background") {
|
||||
if (first && first->type == StyleLayerType::Background) {
|
||||
return first->getProperties<BackgroundProperties>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ StyleLayer::StyleLayer(const std::string &id, std::map<ClassID, ClassProperties>
|
||||
: id(id), styles(std::move(styles)), rasterize(std::move(rasterize)) {}
|
||||
|
||||
bool StyleLayer::isBackground() const {
|
||||
return type == StyleLayerType::Background && id == "background";
|
||||
return type == StyleLayerType::Background;
|
||||
}
|
||||
|
||||
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now,
|
||||
@@ -191,24 +191,30 @@ void StyleLayer::applyStyleProperties<LineProperties>(const float z, const times
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<IconProperties>(const float z, const timestamp now) {
|
||||
properties.set<IconProperties>();
|
||||
IconProperties &icon = properties.get<IconProperties>();
|
||||
applyStyleProperty(PropertyKey::IconOpacity, icon.opacity, z, now);
|
||||
applyStyleProperty(PropertyKey::IconRotate, icon.rotate, z, now);
|
||||
applyStyleProperty(PropertyKey::IconRotateAnchor, icon.rotate_anchor, z, now);
|
||||
}
|
||||
void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const timestamp now) {
|
||||
properties.set<SymbolProperties>();
|
||||
SymbolProperties &symbol = properties.get<SymbolProperties>();
|
||||
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<TextProperties>(const float z, const timestamp now) {
|
||||
properties.set<TextProperties>();
|
||||
TextProperties &text = properties.get<TextProperties>();
|
||||
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 <>
|
||||
@@ -248,8 +254,7 @@ void StyleLayer::updateProperties(float z, const timestamp now) {
|
||||
switch (type) {
|
||||
case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now); break;
|
||||
case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now); break;
|
||||
case StyleLayerType::Icon: applyStyleProperties<IconProperties>(z, now); break;
|
||||
case StyleLayerType::Text: applyStyleProperties<TextProperties>(z, now); break;
|
||||
case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now); break;
|
||||
case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now); break;
|
||||
case StyleLayerType::Composite: applyStyleProperties<CompositeProperties>(z, now); break;
|
||||
case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now); break;
|
||||
|
||||
@@ -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<float> &targe
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, typename Parser>
|
||||
bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser) {
|
||||
template<typename Parser, typename T>
|
||||
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 {
|
||||
Log::Warning(Event::ParseStyle, "%s must have one of the enum values", 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<SourceTypeClass>(itr->value, type, "type");
|
||||
parseRenderProperty(itr->value, url, "url");
|
||||
if (type == SourceType::Raster) {
|
||||
parseRenderProperty(itr->value, tile_size, "tileSize");
|
||||
@@ -175,7 +189,7 @@ void StyleParser::parseSources(JSVal value) {
|
||||
parseRenderProperty(itr->value, min_zoom, "minZoom");
|
||||
parseRenderProperty(itr->value, max_zoom, "maxZoom");
|
||||
|
||||
sources.emplace(std::move(name), std::make_shared<StyleSource>(type, url, tile_size, min_zoom, max_zoom));
|
||||
sources.emplace(std::move(name), std::make_shared<StyleSource>(SourceInfo { type, url, tile_size, min_zoom, max_zoom }));
|
||||
}
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "sources must be an object");
|
||||
@@ -348,22 +362,22 @@ template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value,
|
||||
template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVal value, const char *property_name) {
|
||||
if (!value.IsString()) {
|
||||
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
|
||||
return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Default };
|
||||
return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Map };
|
||||
}
|
||||
|
||||
return std::tuple<bool, TranslateAnchorType> { true, parseTranslateAnchorType({ value.GetString(), value.GetStringLength() }) };
|
||||
return std::tuple<bool, TranslateAnchorType> { true, TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateAnchorType>(JSVal value, const char *property_name) {
|
||||
if (!value.IsString()) {
|
||||
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
|
||||
return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Default };
|
||||
return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Map };
|
||||
}
|
||||
|
||||
return std::tuple<bool, RotateAnchorType> { true, parseRotateAnchorType({ value.GetString(), value.GetStringLength() }) };
|
||||
return std::tuple<bool, RotateAnchorType> { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char *property_name) {
|
||||
template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char */*property_name*/) {
|
||||
PropertyTransition transition;
|
||||
if (value.IsObject()) {
|
||||
if (value.HasMember("duration") && value["duration"].IsNumber()) {
|
||||
@@ -592,7 +606,18 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) {
|
||||
parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-opacity", Key::IconOpacity, klass, value);
|
||||
parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
|
||||
parseOptionalProperty<RotateAnchorType>("icon-rotate-anchor", Key::IconRotateAnchor, klass, value);
|
||||
parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-size", Key::IconSize, klass, value);
|
||||
parseOptionalProperty<Function<Color>>("icon-color", Key::IconColor, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-color", Key::IconColor, klass, value);
|
||||
parseOptionalProperty<Function<Color>>("icon-halo-color", Key::IconHaloColor, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-halo-color", Key::IconHaloColor, klass, value);
|
||||
parseOptionalProperty<Function<float>>("icon-halo-width", Key::IconHaloWidth, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-halo-width", Key::IconHaloWidth, klass, value);
|
||||
parseOptionalProperty<Function<float>>("icon-halo-blur", Key::IconHaloBlur, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-halo-blur", Key::IconHaloBlur, klass, value);
|
||||
parseOptionalProperty<Function<float>>("icon-translate", { Key::IconTranslateX, Key::IconTranslateY }, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-icon-translate", Key::IconTranslate, klass, value);
|
||||
|
||||
parseOptionalProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-text-opacity", Key::TextOpacity, klass, value);
|
||||
@@ -606,6 +631,8 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) {
|
||||
parseOptionalProperty<PropertyTransition>("transition-text-halo-width", Key::TextHaloWidth, klass, value);
|
||||
parseOptionalProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-text-halo-blur", Key::TextHaloBlur, klass, value);
|
||||
parseOptionalProperty<Function<float>>("text-translate", { Key::TextTranslateX, Key::TextTranslateY }, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-text-translate", Key::TextTranslate, klass, value);
|
||||
|
||||
parseOptionalProperty<Function<float>>("composite-opacity", Key::CompositeOpacity, klass, value);
|
||||
parseOptionalProperty<PropertyTransition>("transition-composite-opacity", Key::CompositeOpacity, klass, value);
|
||||
@@ -714,6 +741,24 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
|
||||
JSVal value_render = replaceConstant(value["render"]);
|
||||
parseRender(value_render, layer);
|
||||
}
|
||||
|
||||
if (value.HasMember("min-zoom")) {
|
||||
JSVal min_zoom = value["min-zoom"];
|
||||
if (min_zoom.IsNumber()) {
|
||||
layer->bucket->min_zoom = min_zoom.GetDouble();
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "min-zoom of layer %s must be numeric", layer->id.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (value.HasMember("max-zoom")) {
|
||||
JSVal max_zoom = value["max-zoom"];
|
||||
if (max_zoom.IsNumber()) {
|
||||
layer->bucket->min_zoom = max_zoom.GetDouble();
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "max-zoom of layer %s must be numeric", layer->id.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterExpression StyleParser::parseFilter(JSVal value) {
|
||||
@@ -815,61 +860,70 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
|
||||
case StyleLayerType::Fill: {
|
||||
StyleBucketFill &render = bucket.render.get<StyleBucketFill>();
|
||||
|
||||
parseRenderProperty(value, render.winding, "fill-winding", parseWindingType);
|
||||
parseRenderProperty<WindingTypeClass>(value, render.winding, "fill-winding");
|
||||
} break;
|
||||
|
||||
case StyleLayerType::Line: {
|
||||
StyleBucketLine &render = bucket.render.get<StyleBucketLine>();
|
||||
|
||||
parseRenderProperty(value, render.cap, "line-cap", parseCapType);
|
||||
parseRenderProperty(value, render.join, "line-join", parseJoinType);
|
||||
parseRenderProperty<CapTypeClass>(value, render.cap, "line-cap");
|
||||
parseRenderProperty<JoinTypeClass>(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<StyleBucketIcon>();
|
||||
case StyleLayerType::Symbol: {
|
||||
StyleBucketSymbol &render = bucket.render.get<StyleBucketSymbol>();
|
||||
|
||||
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<PlacementTypeClass>(value, render.placement, "symbol-placement");
|
||||
if (render.placement == PlacementType::Line) {
|
||||
// Change the default value in case of line placement.
|
||||
render.text.rotation_alignment = RotationAlignmentType::Map;
|
||||
render.icon.rotation_alignment = RotationAlignmentType::Map;
|
||||
}
|
||||
parseRenderProperty(value, render.translate_anchor, "icon-translate-anchor", parseTranslateAnchorType);
|
||||
} break;
|
||||
|
||||
case StyleLayerType::Text: {
|
||||
StyleBucketText &render = bucket.render.get<StyleBucketText>();
|
||||
parseRenderProperty(value, render.min_distance, "symbol-min-distance");
|
||||
|
||||
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
|
||||
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<RotationAlignmentTypeClass>(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.rotate, "icon-rotate");
|
||||
parseRenderProperty(value, render.icon.padding, "icon-padding");
|
||||
parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright");
|
||||
parseRenderProperty(value, render.icon.offset, "icon-offset");
|
||||
parseRenderProperty<TranslateAnchorTypeClass>(value, render.icon.translate_anchor, "icon-translate-anchor");
|
||||
|
||||
|
||||
parseRenderProperty<RotationAlignmentTypeClass>(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.line_height, "text-line-height")) {
|
||||
render.line_height *= 24; // em
|
||||
if (parseRenderProperty(value, render.text.line_height, "text-line-height")) {
|
||||
render.text.line_height *= 24; // em
|
||||
}
|
||||
if (parseRenderProperty(value, render.letter_spacing, "text-letter-spacing")) {
|
||||
render.letter_spacing *= 24; // em
|
||||
if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) {
|
||||
render.text.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);
|
||||
parseRenderProperty<TextJustifyTypeClass>(value, render.text.justify, "text-justify");
|
||||
parseRenderProperty<TextHorizontalAlignTypeClass>(value, render.text.horizontal_align, "text-horizontal-align");
|
||||
parseRenderProperty<TextVerticalAlignTypeClass>(value, render.text.vertical_align, "text-vertical-align");
|
||||
parseRenderProperty(value, render.text.max_angle, "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<TextTransformTypeClass>(value, render.text.transform, "text-transform");
|
||||
parseRenderProperty(value, render.text.offset, "text-offset");
|
||||
parseRenderProperty<TranslateAnchorTypeClass>(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.
|
||||
|
||||
@@ -4,8 +4,7 @@ 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; }
|
||||
template<> const BackgroundProperties &defaultStyleProperties() { static const BackgroundProperties p; return p; }
|
||||
|
||||
@@ -31,37 +31,59 @@ Collision::~Collision() {
|
||||
delete ((Tree *)hTree);
|
||||
}
|
||||
|
||||
Collision::Collision() : cTree(new Tree()), hTree(new Tree()) {
|
||||
Collision::Collision(float zoom, float tileExtent, float tileSize, float placementDepth)
|
||||
: hTree(new Tree()), // tree for horizontal labels
|
||||
cTree(new Tree()), // tree for glyphs from curved labels
|
||||
|
||||
// tile pixels per screen pixels at the tile's zoom level
|
||||
tilePixelRatio(tileExtent / tileSize),
|
||||
|
||||
zoom(zoom),
|
||||
|
||||
// Calculate the maximum scale we can go down in our fake-3d rtree so that
|
||||
// placement still makes sense. This is calculated so that the minimum
|
||||
// placement zoom can be at most 25.5 (we use an unsigned integer x10 to
|
||||
// store the minimum zoom).
|
||||
//
|
||||
// We don't want to place labels all the way to 25.5. This lets too many
|
||||
// glyphs be placed, slowing down collision checking. Only place labels if
|
||||
// they will show up within the intended zoom range of the tile.
|
||||
maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom))) {
|
||||
const float m = 4096;
|
||||
const float edge = m * tilePixelRatio * 2;
|
||||
|
||||
// Hack to prevent cross-tile labels
|
||||
insert(
|
||||
{{GlyphBox{
|
||||
CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}},
|
||||
CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}}, 0},
|
||||
GlyphBox{
|
||||
CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}},
|
||||
CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}},
|
||||
0}}},
|
||||
CollisionAnchor(0, 0), 1, {{M_PI * 2, 0}}, false, 2);
|
||||
/* glyphs */ {
|
||||
GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{0, edge}},
|
||||
/* minScale */ 0,
|
||||
/* padding */ 2},
|
||||
GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{edge, 0}},
|
||||
/* minScale */ 0,
|
||||
/* padding */ 2}},
|
||||
/* anchor */ CollisionAnchor(0, 0),
|
||||
/* placementScale */ 1,
|
||||
/* placementRange */ {{M_PI * 2, 0}},
|
||||
/* horizontal */ false);
|
||||
|
||||
insert({{GlyphBox{
|
||||
CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}},
|
||||
CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}},
|
||||
0},
|
||||
GlyphBox{
|
||||
CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}},
|
||||
CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}},
|
||||
0}}},
|
||||
CollisionAnchor{m, m}, 1, {{M_PI * 2, 0}}, false, 2);
|
||||
insert(
|
||||
/* glyphs */ {
|
||||
GlyphBox{/* box*/ CollisionRect{CollisionPoint{-edge, 0}, CollisionPoint{edge, edge}},
|
||||
/* minScale */ 0,
|
||||
/* padding */ 2},
|
||||
GlyphBox{/* box */ CollisionRect{CollisionPoint{0, -edge}, CollisionPoint{edge, edge}},
|
||||
/* minScale */ 0,
|
||||
/* padding */ 2}},
|
||||
/* anchor */ CollisionAnchor(m, m),
|
||||
/* placementScale */ 1,
|
||||
/* placementRange */ {{M_PI * 2, 0}},
|
||||
/* horizontal */ false);
|
||||
}
|
||||
|
||||
GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, bool horizontal,
|
||||
const CollisionAnchor &anchor) {
|
||||
GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) {
|
||||
GlyphBox mergedGlyphs;
|
||||
const float inf = std::numeric_limits<float>::infinity();
|
||||
mergedGlyphs.box = CollisionRect{{inf, inf}, {-inf, -inf}};
|
||||
mergedGlyphs.rotate = horizontal;
|
||||
mergedGlyphs.anchor = anchor;
|
||||
|
||||
CollisionRect &box = mergedGlyphs.box;
|
||||
@@ -71,105 +93,51 @@ GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, bool horizontal,
|
||||
box.tl.y = util::min(box.tl.y, gbox.tl.y);
|
||||
box.br.x = util::max(box.br.x, gbox.br.x);
|
||||
box.br.y = util::max(box.br.y, gbox.br.y);
|
||||
mergedGlyphs.minScale =
|
||||
util::max(mergedGlyphs.minScale, glyph.minScale);
|
||||
mergedGlyphs.minScale = util::max(mergedGlyphs.minScale, glyph.minScale);
|
||||
}
|
||||
|
||||
return mergedGlyphs;
|
||||
}
|
||||
|
||||
PlacementProperty Collision::place(const GlyphBoxes &boxes,
|
||||
const CollisionAnchor &anchor,
|
||||
float minPlacementScale,
|
||||
float maxPlacementScale, float padding,
|
||||
bool horizontal, bool alwaysVisible) {
|
||||
Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minScale,
|
||||
float maxScale) {
|
||||
return Box{
|
||||
Point{
|
||||
anchor.x + util::min(bbox.tl.x / minScale, bbox.tl.x / maxScale),
|
||||
anchor.y + util::min(bbox.tl.y / minScale, bbox.tl.y / maxScale),
|
||||
},
|
||||
Point{
|
||||
anchor.x + util::max(bbox.br.x / minScale, bbox.br.x / maxScale),
|
||||
anchor.y + util::max(bbox.br.y / minScale, bbox.br.y / maxScale),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
float minScale = std::numeric_limits<float>::infinity();
|
||||
for (const GlyphBox &glyphBox : boxes) {
|
||||
minScale = util::min(minScale, glyphBox.minScale);
|
||||
}
|
||||
minPlacementScale = util::max(minPlacementScale, minScale);
|
||||
|
||||
// Collision checks between rotating and fixed labels are
|
||||
// relatively expensive, so we use one box per label, not per glyph
|
||||
// for horizontal labels.
|
||||
GlyphBoxes glyphs;
|
||||
if (horizontal) {
|
||||
glyphs.push_back(getMergedGlyphs(boxes, horizontal, anchor));
|
||||
} else {
|
||||
glyphs = boxes;
|
||||
}
|
||||
|
||||
// Calculate bboxes for all the glyphs
|
||||
for (GlyphBox &glyph : glyphs) {
|
||||
if (horizontal) {
|
||||
const CollisionRect &box = glyph.box;
|
||||
float x12 = box.tl.x * box.tl.x, y12 = box.tl.y * box.tl.y,
|
||||
x22 = box.br.x * box.br.x, y22 = box.br.y * box.br.y,
|
||||
diag = std::sqrt(
|
||||
util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22));
|
||||
|
||||
glyph.bbox = CollisionRect{{-diag, -diag}, {diag, diag}};
|
||||
} else {
|
||||
glyph.bbox = glyph.box;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the minimum scale the entire label can be shown without
|
||||
// collisions
|
||||
float scale = alwaysVisible ? minPlacementScale
|
||||
: getPlacementScale(glyphs, minPlacementScale,
|
||||
maxPlacementScale, padding);
|
||||
|
||||
// Return if the label can never be placed without collision
|
||||
if (scale < 0 || scale == std::numeric_limits<float>::infinity()) {
|
||||
return PlacementProperty{};
|
||||
}
|
||||
|
||||
// Calculate the range it is safe to rotate all glyphs
|
||||
PlacementRange rotationRange = getPlacementRange(glyphs, scale, horizontal);
|
||||
insert(glyphs, anchor, scale, rotationRange, horizontal, padding);
|
||||
|
||||
float zoom = log(scale) / log(2);
|
||||
|
||||
return PlacementProperty{zoom, rotationRange};
|
||||
}
|
||||
|
||||
float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
float minPlacementScale,
|
||||
float maxPlacementScale, float pad) {
|
||||
float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale) {
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &bbox = glyph.bbox;
|
||||
const CollisionRect &box = glyph.box;
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
const CollisionAnchor &anchor = glyph.anchor;
|
||||
const float pad = glyph.padding;
|
||||
|
||||
if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 ||
|
||||
anchor.y > 4096) {
|
||||
return -1;
|
||||
if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float minScale = std::fmax(minPlacementScale, glyph.minScale);
|
||||
float maxScale = glyph.maxScale;
|
||||
float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity();
|
||||
|
||||
if (minScale >= maxScale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the scaled bounding box of the unrotated glyph
|
||||
float minPlacedX = anchor.x + bbox.tl.x / minScale;
|
||||
float minPlacedY = anchor.y + bbox.tl.y / minScale;
|
||||
float maxPlacedX = anchor.x + bbox.br.x / minScale;
|
||||
float maxPlacedY = anchor.y + bbox.br.y / minScale;
|
||||
|
||||
Box query_box{Point{minPlacedX, minPlacedY},
|
||||
Point{maxPlacedX, maxPlacedY}};
|
||||
const Box searchBox = getBox(anchor, bbox, minScale, maxScale);
|
||||
|
||||
std::vector<PlacementValue> blocking;
|
||||
((Tree *)cTree)
|
||||
->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
((Tree *)hTree)
|
||||
->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
((Tree *)hTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking));
|
||||
((Tree *)cTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking));
|
||||
|
||||
if (blocking.size()) {
|
||||
const CollisionAnchor &na = anchor; // new anchor
|
||||
@@ -184,7 +152,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
// NOTE: this isn't right because there can be glyphs with
|
||||
// the same anchor but differing box offsets.
|
||||
if (na == oa) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: unhardcode the 8 = tileExtent/tileSize
|
||||
@@ -192,17 +160,13 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
|
||||
// Original algorithm:
|
||||
float s1 = (ob.tl.x - nb.br.x - padding) /
|
||||
(na.x - oa.x); // scale at which new box is to the
|
||||
// left of old box
|
||||
(na.x - oa.x); // scale at which new box is to the left of old box
|
||||
float s2 = (ob.br.x - nb.tl.x + padding) /
|
||||
(na.x - oa.x); // scale at which new box is to the
|
||||
// right of old box
|
||||
(na.x - oa.x); // scale at which new box is to the right of old box
|
||||
float s3 = (ob.tl.y - nb.br.y - padding) /
|
||||
(na.y - oa.y); // scale at which new box is to the
|
||||
// top of old box
|
||||
(na.y - oa.y); // scale at which new box is to the top of old box
|
||||
float s4 = (ob.br.y - nb.tl.y + padding) /
|
||||
(na.y - oa.y); // scale at which new box is to the
|
||||
// bottom of old box
|
||||
(na.y - oa.y); // scale at which new box is to the bottom of old box
|
||||
|
||||
if (isnan(s1) || isnan(s2)) {
|
||||
s1 = s2 = 1;
|
||||
@@ -211,7 +175,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
s3 = s4 = 1;
|
||||
}
|
||||
|
||||
float collisionFreeScale = std::fmin(std::fmax(s1, s2), std::fmax(s3, s4));
|
||||
const float collisionFreeScale = std::fmin(std::fmax(s1, s2), std::fmax(s3, s4));
|
||||
|
||||
// Only update label's min scale if the glyph was
|
||||
// restricted by a collision
|
||||
@@ -223,7 +187,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
}
|
||||
|
||||
if (minPlacementScale > maxPlacementScale) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,13 +196,12 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
|
||||
return minPlacementScale;
|
||||
}
|
||||
|
||||
PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
|
||||
float placementScale,
|
||||
PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
|
||||
bool horizontal) {
|
||||
PlacementRange placementRange = {{2.0f * M_PI, 0}};
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &bbox = glyph.bbox;
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
const CollisionAnchor &anchor = glyph.anchor;
|
||||
|
||||
float minPlacedX = anchor.x + bbox.tl.x / placementScale;
|
||||
@@ -246,30 +209,28 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
|
||||
float maxPlacedX = anchor.x + bbox.br.x / placementScale;
|
||||
float maxPlacedY = anchor.y + bbox.br.y / placementScale;
|
||||
|
||||
Box query_box{Point{minPlacedX, minPlacedY},
|
||||
Point{maxPlacedX, maxPlacedY}};
|
||||
Box query_box{Point{minPlacedX, minPlacedY}, Point{maxPlacedX, maxPlacedY}};
|
||||
|
||||
std::vector<PlacementValue> blocking;
|
||||
((Tree *)hTree)
|
||||
->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
((Tree *)hTree)->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
|
||||
if (horizontal) {
|
||||
((Tree *)cTree)
|
||||
->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
((Tree *)cTree)->query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
}
|
||||
|
||||
for (const PlacementValue &value : blocking) {
|
||||
const Box &s = std::get<0>(value);
|
||||
const PlacementBox &b = std::get<1>(value);
|
||||
const CollisionRect &bbox2 = b.hBox ? b.hBox.get() : b.box;
|
||||
|
||||
float x1, x2, y1, y2, intersectX, intersectY;
|
||||
|
||||
// Adjust and compare bboxes to see if the glyphs might intersect
|
||||
if (placementScale > b.placementScale) {
|
||||
x1 = b.anchor.x + b.bbox.tl.x / placementScale;
|
||||
y1 = b.anchor.y + b.bbox.tl.y / placementScale;
|
||||
x2 = b.anchor.x + b.bbox.br.x / placementScale;
|
||||
y2 = b.anchor.y + b.bbox.br.y / placementScale;
|
||||
x1 = b.anchor.x + bbox2.tl.x / placementScale;
|
||||
y1 = b.anchor.y + bbox2.tl.y / placementScale;
|
||||
x2 = b.anchor.x + bbox2.br.x / placementScale;
|
||||
y2 = b.anchor.y + bbox2.br.y / placementScale;
|
||||
intersectX = x1 < maxPlacedX && x2 > minPlacedX;
|
||||
intersectY = y1 < maxPlacedY && y2 > minPlacedY;
|
||||
} else {
|
||||
@@ -278,10 +239,8 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
|
||||
x2 = anchor.x + bbox.br.x / b.placementScale;
|
||||
y2 = anchor.y + bbox.br.y / b.placementScale;
|
||||
|
||||
intersectX = x1 < s.max_corner().get<0>() &&
|
||||
x2 > s.min_corner().get<0>();
|
||||
intersectY = y1 < s.max_corner().get<1>() &&
|
||||
y2 > s.min_corner().get<1>();
|
||||
intersectX = x1 < s.max_corner().get<0>() && x2 > s.min_corner().get<0>();
|
||||
intersectY = y1 < s.max_corner().get<1>() && y2 > s.min_corner().get<1>();
|
||||
}
|
||||
|
||||
// If they can't intersect, skip more expensive rotation calculation
|
||||
@@ -301,45 +260,40 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
|
||||
};
|
||||
|
||||
void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
|
||||
float placementScale,
|
||||
const PlacementRange &placementRange, bool horizontal,
|
||||
float padding) {
|
||||
float placementScale, const PlacementRange &placementRange,
|
||||
bool horizontal) {
|
||||
assert(placementScale != std::numeric_limits<float>::infinity());
|
||||
|
||||
std::vector<PlacementValue> result;
|
||||
result.reserve(glyphs.size());
|
||||
std::vector<PlacementValue> allBounds;
|
||||
allBounds.reserve(glyphs.size());
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &bbox = glyph.bbox;
|
||||
const CollisionRect &box = glyph.box;
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
|
||||
float minScale = std::fmax(placementScale, glyph.minScale);
|
||||
const float minScale = util::max(placementScale, glyph.minScale);
|
||||
const float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity();
|
||||
|
||||
Box bounds{Point{anchor.x + bbox.tl.x / minScale,
|
||||
anchor.y + bbox.tl.y / minScale},
|
||||
Point{anchor.x + bbox.br.x / minScale,
|
||||
anchor.y + bbox.br.y / minScale}};
|
||||
const Box bounds = getBox(anchor, bbox, minScale, maxScale);
|
||||
|
||||
PlacementBox placement;
|
||||
placement.anchor = anchor;
|
||||
placement.box = box;
|
||||
placement.bbox = bbox;
|
||||
placement.rotate = horizontal;
|
||||
if (glyph.hBox) {
|
||||
placement.hBox = bbox;
|
||||
}
|
||||
placement.placementRange = placementRange;
|
||||
placement.placementScale = minScale;
|
||||
placement.maxScale = glyph.maxScale;
|
||||
placement.padding = padding;
|
||||
placement.maxScale = maxScale;
|
||||
placement.padding = glyph.padding;
|
||||
|
||||
assert(!isnan(bounds.min_corner().get<0>()) && !isnan(bounds.min_corner().get<1>()));
|
||||
assert(!isnan(bounds.max_corner().get<0>()) && !isnan(bounds.max_corner().get<1>()));
|
||||
|
||||
result.emplace_back(bounds, placement);
|
||||
allBounds.emplace_back(bounds, placement);
|
||||
}
|
||||
|
||||
// Bulk-insert all glyph boxes
|
||||
if (horizontal) {
|
||||
((Tree *)hTree)->insert(result.begin(), result.end());
|
||||
((Tree *)hTree)->insert(allBounds.begin(), allBounds.end());
|
||||
} else {
|
||||
((Tree *)cTree)->insert(result.begin(), result.end());
|
||||
((Tree *)cTree)->insert(allBounds.begin(), allBounds.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <uv.h>
|
||||
#include <algorithm>
|
||||
@@ -30,93 +32,111 @@ const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
|
||||
return sdfs;
|
||||
}
|
||||
|
||||
const Shaping FontStack::getShaping(const std::u32string &string, const float &maxWidth,
|
||||
const float &lineHeight, const float &alignment, const float &verticalAlignment,
|
||||
const float &letterSpacing) const {
|
||||
|
||||
const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
|
||||
const float lineHeight, const float horizontalAlign,
|
||||
const float verticalAlign, const float justify,
|
||||
const float spacing, const vec2<float> &translate) const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
uint32_t i = 0;
|
||||
uint32_t x = 0;
|
||||
|
||||
Shaping shaping;
|
||||
|
||||
int32_t x = std::round(translate.x * 24); // one em
|
||||
const int32_t y = std::round(translate.y * 24); // one em
|
||||
|
||||
// Loop through all characters of this label and shape.
|
||||
for (uint32_t chr : string) {
|
||||
shaping.emplace_back(0, chr, x, 0);
|
||||
i++;
|
||||
shaping.emplace_back(chr, x, y);
|
||||
auto metric = metrics.find(chr);
|
||||
if (metric != metrics.end()) {
|
||||
x += metric->second.advance + letterSpacing;
|
||||
x += metric->second.advance + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
lineWrap(shaping, lineHeight, maxWidth, alignment, verticalAlignment);
|
||||
if (!shaping.size())
|
||||
return shaping;
|
||||
|
||||
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
|
||||
|
||||
return shaping;
|
||||
}
|
||||
|
||||
void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineHeight, const float &verticalAlign) {
|
||||
const float dy = -(lineHeight * (lines - 1) + 24) * verticalAlign - 5;
|
||||
for (GlyphPlacement &shape : shaping) {
|
||||
shape.y += dy;
|
||||
void align(Shaping &shaping, const float justify, const float horizontalAlign,
|
||||
const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
|
||||
const uint32_t line) {
|
||||
const float shiftX = (justify - horizontalAlign) * maxLineLength;
|
||||
const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
|
||||
|
||||
for (PositionedGlyph &glyph : shaping) {
|
||||
glyph.x += shiftX;
|
||||
glyph.y += shiftY;
|
||||
}
|
||||
}
|
||||
|
||||
void alignHorizontally(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics,
|
||||
const uint32_t &start, const uint32_t &end, const float &alignment) {
|
||||
auto & shape = shaping.at(end);
|
||||
auto metric = metrics.find(shape.glyph);
|
||||
void justifyLine(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
|
||||
uint32_t end, float justify) {
|
||||
PositionedGlyph &glyph = shaping[end];
|
||||
auto metric = metrics.find(glyph.glyph);
|
||||
if (metric != metrics.end()) {
|
||||
uint32_t lastAdvance = metric->second.advance;
|
||||
int32_t lineIndent = (shape.x + lastAdvance) * alignment;
|
||||
const uint32_t lastAdvance = metric->second.advance;
|
||||
const float lineIndent = float(glyph.x + lastAdvance) * justify;
|
||||
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
shaping[j].x -= lineIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth,
|
||||
const float &alignment, const float &verticalAlignment) const {
|
||||
|
||||
std::size_t num_shapes = shaping.size();
|
||||
if (!num_shapes) {
|
||||
return;
|
||||
}
|
||||
void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
|
||||
const float horizontalAlign, const float verticalAlign,
|
||||
const float justify) const {
|
||||
uint32_t lastSafeBreak = 0;
|
||||
|
||||
uint32_t lengthBeforeCurrentLine = 0;
|
||||
uint32_t lineStartIndex = 0;
|
||||
uint32_t line = 0;
|
||||
|
||||
for (uint32_t i = 0; i < shaping.size(); i++) {
|
||||
GlyphPlacement &shape = shaping[i];
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
uint32_t maxLineLength = 0;
|
||||
|
||||
if (shape.x > maxWidth && lastSafeBreak > 0) {
|
||||
if (maxWidth) {
|
||||
for (uint32_t i = 0; i < shaping.size(); i++) {
|
||||
PositionedGlyph &shape = shaping[i];
|
||||
|
||||
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
|
||||
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
|
||||
shaping[k].y += lineHeight;
|
||||
shaping[k].x -= lineLength;
|
||||
if (shape.x > maxWidth && lastSafeBreak > 0) {
|
||||
|
||||
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
|
||||
maxLineLength = util::max(lineLength, maxLineLength);
|
||||
|
||||
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
|
||||
shaping[k].y += lineHeight;
|
||||
shaping[k].x -= lineLength;
|
||||
}
|
||||
|
||||
if (justify) {
|
||||
justifyLine(shaping, metrics, lineStartIndex, lastSafeBreak - 1, justify);
|
||||
}
|
||||
|
||||
lineStartIndex = lastSafeBreak + 1;
|
||||
lastSafeBreak = 0;
|
||||
lengthBeforeCurrentLine += lineLength;
|
||||
line++;
|
||||
}
|
||||
|
||||
if (alignment) {
|
||||
alignHorizontally(shaping, metrics, lineStartIndex, lastSafeBreak - 1, alignment);
|
||||
if (shape.glyph == 32) {
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
|
||||
lineStartIndex = lastSafeBreak + 1;
|
||||
lastSafeBreak = 0;
|
||||
lengthBeforeCurrentLine += lineLength;
|
||||
line++;
|
||||
}
|
||||
if (shape.glyph == 32) {
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
}
|
||||
alignHorizontally(shaping, metrics, lineStartIndex, shaping.size() - 1, alignment);
|
||||
alignVertically(shaping, line + 1, lineHeight, verticalAlignment);
|
||||
|
||||
maxLineLength = maxLineLength || shaping.back().x;
|
||||
|
||||
justifyLine(shaping, metrics, lineStartIndex, shaping.size() - 1, justify);
|
||||
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
|
||||
}
|
||||
|
||||
GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange)
|
||||
GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource)
|
||||
: future(promise.get_future().share())
|
||||
{
|
||||
// Load the glyph set URL
|
||||
@@ -133,7 +153,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
|
||||
fprintf(stderr, "%s\n", url.c_str());
|
||||
#endif
|
||||
|
||||
platform::request_http(url, [&](platform::Response *res) {
|
||||
fileSource->load(ResourceType::Glyphs, url, [&](platform::Response *res) {
|
||||
if (res->code != 200) {
|
||||
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
|
||||
const std::string msg = util::sprintf<255>("[ERROR] failed to load glyphs (%d): %s\n", res->code, res->error_message.c_str());
|
||||
@@ -207,12 +227,19 @@ void GlyphPBF::parse(FontStack &stack) {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
GlyphStore::GlyphStore(const std::string &glyphURL)
|
||||
: glyphURL(glyphURL) {}
|
||||
GlyphStore::GlyphStore(const std::shared_ptr<FileSource> &fileSource) : fileSource(fileSource) {}
|
||||
|
||||
void GlyphStore::setURL(const std::string &url) {
|
||||
glyphURL = url;
|
||||
}
|
||||
|
||||
|
||||
void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges) {
|
||||
// We are implementing a blocking wait with futures: Every GlyphSet has a future that we are
|
||||
// waiting for until it is loaded.
|
||||
if (glyphRanges.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FontStack *stack = nullptr;
|
||||
|
||||
@@ -243,7 +270,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon
|
||||
auto range_it = rangeSets.find(range);
|
||||
if (range_it == rangeSets.end()) {
|
||||
// We don't have this glyph set yet for this font stack.
|
||||
range_it = rangeSets.emplace(range, std::make_unique<GlyphPBF>(glyphURL, fontStack, range)).first;
|
||||
range_it = rangeSets.emplace(range, std::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first;
|
||||
}
|
||||
|
||||
return range_it->second->getFuture();
|
||||
|
||||
@@ -1,73 +1,34 @@
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/geometry/interpolate.hpp>
|
||||
#include <mbgl/renderer/text_bucket.hpp>
|
||||
#include <mbgl/geometry/anchor.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
namespace mbgl {
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
const int Placement::tileExtent = 4096;
|
||||
const int Placement::glyphSize =
|
||||
24; // size in pixels of this glyphs in the tile
|
||||
const float Placement::minScale = 0.5; // underscale by 1 zoom level
|
||||
|
||||
Placement::Placement(int8_t zoom)
|
||||
: zoom(zoom),
|
||||
zOffset(log(256.0 / util::tileSize) / log(2)),
|
||||
|
||||
// Calculate the maximum scale we can go down in our fake-3d rtree so that
|
||||
// placement still makes sense. This is calculated so that the minimum
|
||||
// placement zoom can be at most 25.5 (we use an unsigned integer x10 to
|
||||
// store the minimum zoom).
|
||||
//
|
||||
// We don't want to place labels all the way to 25.5. This lets too many
|
||||
// glyphs be placed, slowing down collision checking. Only place labels if
|
||||
// they will show up within the intended zoom range of the tile.
|
||||
// TODO make this not hardcoded to 3
|
||||
maxPlacementScale(std::exp(log(2) * util::min((25.5 - zoom), 3.0))) {}
|
||||
|
||||
bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; }
|
||||
|
||||
static const Glyph null_glyph;
|
||||
|
||||
inline const Glyph &getGlyph(const GlyphPlacement &placed,
|
||||
const GlyphPositions &face) {
|
||||
auto it = face.find(placed.glyph);
|
||||
if (it != face.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
fprintf(stderr, "glyph %d does not exist\n", placed.glyph);
|
||||
}
|
||||
|
||||
return null_glyph;
|
||||
}
|
||||
const float Placement::globalMinScale = 0.5; // underscale by 1 zoom level
|
||||
|
||||
struct GlyphInstance {
|
||||
explicit GlyphInstance(const vec2<float> &anchor) : anchor(anchor) {}
|
||||
explicit GlyphInstance(const vec2<float> &anchor, float offset,
|
||||
float minScale, float maxScale, float angle)
|
||||
: anchor(anchor),
|
||||
offset(offset),
|
||||
minScale(minScale),
|
||||
maxScale(maxScale),
|
||||
angle(angle) {}
|
||||
explicit GlyphInstance(const vec2<float> &anchor, float offset, float minScale, float maxScale,
|
||||
float angle)
|
||||
: anchor(anchor), offset(offset), minScale(minScale), maxScale(maxScale), angle(angle) {}
|
||||
|
||||
const vec2<float> anchor;
|
||||
const float offset = 0.0f;
|
||||
const float minScale = Placement::minScale;
|
||||
const float minScale = Placement::globalMinScale;
|
||||
const float maxScale = std::numeric_limits<float>::infinity();
|
||||
const float angle = 0.0f;
|
||||
};
|
||||
|
||||
typedef std::vector<GlyphInstance> GlyphInstances;
|
||||
|
||||
void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
|
||||
Anchor &anchor, float offset,
|
||||
const std::vector<Coordinate> &line, int segment,
|
||||
int8_t direction, float maxAngleDelta) {
|
||||
void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &anchor,
|
||||
float offset, const std::vector<Coordinate> &line, int segment,
|
||||
int8_t direction, float maxAngle) {
|
||||
const bool upsideDown = direction < 0;
|
||||
|
||||
if (offset < 0)
|
||||
@@ -92,14 +53,14 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
|
||||
while (true) {
|
||||
const float dist = util::dist<float>(newAnchor, end);
|
||||
const float scale = offset / dist;
|
||||
float angle = -std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) +
|
||||
direction * M_PI / 2.0f;
|
||||
float angle =
|
||||
-std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) + direction * M_PI / 2.0f;
|
||||
if (upsideDown)
|
||||
angle += M_PI;
|
||||
|
||||
// Don't place around sharp corners
|
||||
float angleDiff = std::fmod((angle - prevAngle), (2.0f * M_PI));
|
||||
if (prevAngle && std::fabs(angleDiff) > maxAngleDelta) {
|
||||
if (prevAngle && std::fabs(angleDiff) > maxAngle) {
|
||||
anchor.scale = prevscale;
|
||||
break;
|
||||
}
|
||||
@@ -109,8 +70,7 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
|
||||
/* offset */ static_cast<float>(upsideDown ? M_PI : 0.0),
|
||||
/* minScale */ scale,
|
||||
/* maxScale */ prevscale,
|
||||
/* angle */ static_cast<float>(
|
||||
std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))};
|
||||
/* angle */ static_cast<float>(std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))};
|
||||
|
||||
if (scale <= placementScale)
|
||||
break;
|
||||
@@ -135,49 +95,152 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
|
||||
}
|
||||
}
|
||||
|
||||
void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
|
||||
Anchor &anchor, vec2<float> origin, const Shaping &shaping,
|
||||
const GlyphPositions &face, float fontScale,
|
||||
bool horizontal, const std::vector<Coordinate> &line,
|
||||
float maxAngleDelta, float rotate) {
|
||||
// The total text advance is the width of this label.
|
||||
GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) {
|
||||
// Collision checks between rotating and fixed labels are relatively expensive,
|
||||
// so we use one box per label, not per glyph for horizontal labels.
|
||||
|
||||
// TODO: allow setting an alignment
|
||||
// var alignment = 'center';
|
||||
// if (alignment == 'center') {
|
||||
// origin.x -= advance / 2;
|
||||
// } else if (alignment == 'right') {
|
||||
// origin.x -= advance;
|
||||
// }
|
||||
const float inf = std::numeric_limits<float>::infinity();
|
||||
|
||||
GlyphBox mergedglyphs{/* box */ CollisionRect{inf, inf, -inf, -inf},
|
||||
/* anchor */ anchor,
|
||||
/* minScale */ 0,
|
||||
/* maxScale */ inf,
|
||||
/* padding */ -inf};
|
||||
|
||||
CollisionRect &box = mergedglyphs.box;
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &gbox = glyph.box;
|
||||
box.tl.x = util::min(box.tl.x, gbox.tl.x);
|
||||
box.tl.y = util::min(box.tl.y, gbox.tl.y);
|
||||
box.br.x = util::max(box.br.x, gbox.br.x);
|
||||
box.br.y = util::max(box.br.y, gbox.br.y);
|
||||
mergedglyphs.minScale = util::max(mergedglyphs.minScale, glyph.minScale);
|
||||
mergedglyphs.padding = util::max(mergedglyphs.padding, glyph.padding);
|
||||
}
|
||||
// for all horizontal labels, calculate bbox covering all rotated positions
|
||||
float x12 = box.tl.x * box.tl.x, y12 = box.tl.y * box.tl.y, x22 = box.br.x * box.br.x,
|
||||
y22 = box.br.y * box.br.y,
|
||||
diag = std::sqrt(util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22));
|
||||
|
||||
mergedglyphs.hBox = CollisionRect{-diag, -diag, diag, diag};
|
||||
|
||||
return mergedglyphs;
|
||||
}
|
||||
|
||||
Placement Placement::getIcon(Anchor &anchor, const Rect<uint16_t> &image, float boxScale,
|
||||
const std::vector<Coordinate> &line, const StyleBucketSymbol &props) {
|
||||
const float x = image.w / 2.0f; // No need to divide by image.pixelRatio here?
|
||||
const float y = image.h / 2.0f; // image.pixelRatio;
|
||||
|
||||
const float dx = props.icon.offset.x;
|
||||
const float dy = props.icon.offset.y;
|
||||
float x1 = (dx - x);
|
||||
float x2 = (dx + x);
|
||||
float y1 = (dy - y);
|
||||
float y2 = (dy + y);
|
||||
|
||||
vec2<float> tl{x1, y1};
|
||||
vec2<float> tr{x2, y1};
|
||||
vec2<float> br{x2, y2};
|
||||
vec2<float> bl{x1, y2};
|
||||
|
||||
float angle = props.icon.rotate * M_PI / 180.0f;
|
||||
if (anchor.segment >= 0 && props.icon.rotation_alignment != RotationAlignmentType::Viewport) {
|
||||
const Coordinate &next = line[anchor.segment];
|
||||
angle += -std::atan2(next.x - anchor.x, next.y - anchor.y) + M_PI / 2;
|
||||
}
|
||||
|
||||
if (angle) {
|
||||
// Compute the transformation matrix.
|
||||
float angle_sin = std::sin(angle);
|
||||
float angle_cos = std::cos(angle);
|
||||
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
|
||||
|
||||
tl = tl.matMul(matrix);
|
||||
tr = tr.matMul(matrix);
|
||||
bl = bl.matMul(matrix);
|
||||
br = br.matMul(matrix);
|
||||
|
||||
x1 = util::min(tl.x, tr.x, bl.x, br.x);
|
||||
x2 = util::max(tl.x, tr.x, bl.x, br.x);
|
||||
y1 = util::min(tl.y, tr.y, bl.y, br.y);
|
||||
y2 = util::max(tl.y, tr.y, bl.y, br.y);
|
||||
}
|
||||
|
||||
const CollisionRect box{/* x1 */ x1 * boxScale,
|
||||
/* y1 */ y1 * boxScale,
|
||||
/* x2 */ x2 * boxScale,
|
||||
/* y2 */ y2 * boxScale};
|
||||
|
||||
Placement placement;
|
||||
|
||||
placement.boxes.emplace_back(
|
||||
/* box */ box,
|
||||
/* anchor */ anchor,
|
||||
/* minScale */ Placement::globalMinScale,
|
||||
/* maxScale */ std::numeric_limits<float>::infinity(),
|
||||
/* padding */ props.icon.padding);
|
||||
|
||||
placement.shapes.emplace_back(
|
||||
/* tl */ tl,
|
||||
/* tr */ tr,
|
||||
/* bl */ bl,
|
||||
/* br */ br,
|
||||
/* image */ image,
|
||||
/* angle */ 0,
|
||||
/* anchors */ anchor,
|
||||
/* minScale */ Placement::globalMinScale,
|
||||
/* maxScale */ std::numeric_limits<float>::infinity());
|
||||
|
||||
placement.minScale = anchor.scale;
|
||||
|
||||
return placement;
|
||||
}
|
||||
|
||||
Placement Placement::getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping,
|
||||
const GlyphPositions &face, float boxScale, bool horizontal,
|
||||
const std::vector<Coordinate> &line,
|
||||
const StyleBucketSymbol &props) {
|
||||
const float maxAngle = props.text.max_angle * M_PI / 180;
|
||||
const float rotate = props.text.rotate * M_PI / 180;
|
||||
const float padding = props.text.padding;
|
||||
const bool alongLine = props.text.rotation_alignment != RotationAlignmentType::Viewport;
|
||||
const bool keepUpright = props.text.keep_upright;
|
||||
|
||||
Placement placement;
|
||||
|
||||
const uint32_t buffer = 3;
|
||||
|
||||
for (const GlyphPlacement &placed : shaping) {
|
||||
const Glyph &glyph = getGlyph(placed, face);
|
||||
if (!glyph) {
|
||||
// This glyph is empty and doesn't have any pixels that we'd need to
|
||||
// show.
|
||||
for (const PositionedGlyph &shape : shaping) {
|
||||
auto face_it = face.find(shape.glyph);
|
||||
if (face_it == face.end())
|
||||
continue;
|
||||
}
|
||||
const Glyph &glyph = face_it->second;
|
||||
const Rect<uint16_t> &rect = glyph.rect;
|
||||
|
||||
float x =
|
||||
(origin.x + placed.x + glyph.metrics.left - buffer + glyph.rect.w / 2) *
|
||||
fontScale;
|
||||
if (!glyph)
|
||||
continue;
|
||||
|
||||
if (!rect)
|
||||
continue;
|
||||
|
||||
const float x = (origin.x + shape.x + glyph.metrics.left - buffer + rect.w / 2) * boxScale;
|
||||
|
||||
GlyphInstances glyphInstances;
|
||||
if (anchor.segment >= 0) {
|
||||
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x,
|
||||
line, anchor.segment, 1, maxAngleDelta);
|
||||
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x,
|
||||
line, anchor.segment, -1, maxAngleDelta);
|
||||
if (anchor.segment >= 0 && alongLine) {
|
||||
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, 1,
|
||||
maxAngle);
|
||||
if (keepUpright)
|
||||
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line,
|
||||
anchor.segment, -1, maxAngle);
|
||||
|
||||
} else {
|
||||
|
||||
glyphInstances.emplace_back(GlyphInstance{anchor});
|
||||
}
|
||||
|
||||
const float x1 = origin.x + placed.x + glyph.metrics.left - buffer;
|
||||
const float y1 = origin.y + placed.y - glyph.metrics.top - buffer;
|
||||
const float x1 = origin.x + shape.x + glyph.metrics.left - buffer;
|
||||
const float y1 = origin.y + shape.y - glyph.metrics.top - buffer;
|
||||
const float x2 = x1 + glyph.rect.w;
|
||||
const float y2 = y1 + glyph.rect.h;
|
||||
|
||||
@@ -186,8 +249,7 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
|
||||
const vec2<float> obl{x1, y2};
|
||||
const vec2<float> obr{x2, y2};
|
||||
|
||||
const CollisionRect obox{fontScale *x1, fontScale *y1,
|
||||
fontScale *x2, fontScale *y2};
|
||||
const CollisionRect obox{boxScale * x1, boxScale * y1, boxScale * x2, boxScale * y2};
|
||||
|
||||
for (const GlyphInstance &instance : glyphInstances) {
|
||||
vec2<float> tl = otl;
|
||||
@@ -204,78 +266,47 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
|
||||
// Compute the transformation matrix.
|
||||
float angle_sin = std::sin(angle);
|
||||
float angle_cos = std::cos(angle);
|
||||
std::array<float, 4> matrix = {
|
||||
{angle_cos, -angle_sin, angle_sin, angle_cos}};
|
||||
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
|
||||
|
||||
tl = tl.matMul(matrix);
|
||||
tr = tr.matMul(matrix);
|
||||
bl = bl.matMul(matrix);
|
||||
br = br.matMul(matrix);
|
||||
|
||||
// Calculate the rotated glyph's bounding box offsets from the
|
||||
// anchor point.
|
||||
box = CollisionRect{
|
||||
fontScale * util::min(tl.x, tr.x, bl.x, br.x),
|
||||
fontScale * util::min(tl.y, tr.y, bl.y, br.y),
|
||||
fontScale * util::max(tl.x, tr.x, bl.x, br.x),
|
||||
fontScale * util::max(tl.y, tr.y, bl.y, br.y)};
|
||||
}
|
||||
|
||||
GlyphBox glyphBox = GlyphBox{
|
||||
box,
|
||||
// Prevent label from extending past the end of the line
|
||||
util::max(instance.minScale, anchor.scale),
|
||||
instance.maxScale,
|
||||
instance.anchor,
|
||||
horizontal};
|
||||
// Prevent label from extending past the end of the line
|
||||
const float glyphMinScale = std::max(instance.minScale, anchor.scale);
|
||||
|
||||
// Remember the glyph for later insertion.
|
||||
glyphs.emplace_back(PlacedGlyph{
|
||||
tl, tr, bl, br, glyph.rect,
|
||||
static_cast<float>(
|
||||
std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))),
|
||||
glyphBox});
|
||||
placement.shapes.emplace_back(
|
||||
tl, tr, bl, br, rect,
|
||||
float(std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))),
|
||||
instance.anchor, glyphMinScale, instance.maxScale);
|
||||
|
||||
if (instance.offset == 0.0f) {
|
||||
boxes.emplace_back(glyphBox);
|
||||
if (!instance.offset) { // not a flipped glyph
|
||||
if (angle) {
|
||||
// Calculate the rotated glyph's bounding box offsets from the anchor point.
|
||||
box = CollisionRect{boxScale * util::min(tl.x, tr.x, bl.x, br.x),
|
||||
boxScale * util::min(tl.y, tr.y, bl.y, br.y),
|
||||
boxScale * util::max(tl.x, tr.x, bl.x, br.x),
|
||||
boxScale * util::max(tl.y, tr.y, bl.y, br.y)};
|
||||
}
|
||||
placement.boxes.emplace_back(box, instance.anchor, glyphMinScale, instance.maxScale, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Placement::addFeature(TextBucket &bucket, const std::vector<Coordinate> &line,
|
||||
const StyleBucketText &info, const GlyphPositions &face,
|
||||
const Shaping &shaping) {
|
||||
const bool horizontal = info.path == TextPathType::Horizontal;
|
||||
const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.max_size);
|
||||
// TODO avoid creating the boxes in the first place?
|
||||
if (horizontal)
|
||||
placement.boxes = {getMergedBoxes(placement.boxes, anchor)};
|
||||
|
||||
Anchors anchors;
|
||||
|
||||
if (line.size() == 1) {
|
||||
// Point labels
|
||||
anchors = Anchors{{Anchor{
|
||||
static_cast<float>(line[0].x), static_cast<float>(line[0].y),
|
||||
0, minScale}}};
|
||||
|
||||
} else {
|
||||
// Line labels
|
||||
anchors = interpolate(line, info.min_distance, minScale);
|
||||
|
||||
// Sort anchors by segment so that we can start placement with
|
||||
// the anchors that can be shown at the lowest zoom levels.
|
||||
std::sort(anchors.begin(), anchors.end(), byScale);
|
||||
const float minPlacementScale = anchor.scale;
|
||||
placement.minScale = std::numeric_limits<float>::infinity();
|
||||
for (const GlyphBox &box : placement.boxes) {
|
||||
placement.minScale = util::min(placement.minScale, box.minScale);
|
||||
}
|
||||
placement.minScale = util::max(minPlacementScale, Placement::globalMinScale);
|
||||
|
||||
for (Anchor anchor : anchors) {
|
||||
PlacedGlyphs glyphs;
|
||||
GlyphBoxes boxes;
|
||||
|
||||
getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, line,
|
||||
info.max_angle_delta, info.rotate);
|
||||
PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
|
||||
info.padding, horizontal, info.always_visible);
|
||||
if (place) {
|
||||
bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset);
|
||||
}
|
||||
}
|
||||
return placement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,14 +239,14 @@ CollisionRange rotationRange(const GlyphBox &inserting,
|
||||
static_cast<float>((b.anchor.y - a.anchor.y) * scale)};
|
||||
|
||||
// Generate a list of collision interval
|
||||
if (a.rotate && b.rotate) {
|
||||
if (a.hBox && b.hBox) {
|
||||
collisions = rotatingRotatingCollisions(a.box, b.box, relativeAnchor);
|
||||
} else if (a.rotate) {
|
||||
} else if (a.hBox) {
|
||||
const CollisionRect box {
|
||||
b.box.tl.x + relativeAnchor.x, b.box.tl.y + relativeAnchor.y,
|
||||
b.box.br.x + relativeAnchor.x, b.box.br.y + relativeAnchor.y};
|
||||
collisions = rotatingFixedCollisions(a.box, box);
|
||||
} else if (b.rotate) {
|
||||
} else if (b.hBox) {
|
||||
const CollisionRect box {
|
||||
a.box.tl.x - relativeAnchor.x, a.box.tl.y - relativeAnchor.y,
|
||||
a.box.br.x - relativeAnchor.x, a.box.br.y - relativeAnchor.y};
|
||||
|
||||
58
src/util/filesource.cpp
Normal file
58
src/util/filesource.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
FileSource::FileSource() {}
|
||||
|
||||
|
||||
void FileSource::setBase(const std::string &value) {
|
||||
base = value;
|
||||
}
|
||||
|
||||
const std::string &FileSource::getBase() const {
|
||||
return base;
|
||||
}
|
||||
|
||||
void FileSource::load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop) {
|
||||
// convert relative URLs to absolute URLs
|
||||
|
||||
const std::string absoluteURL = [&]() -> std::string {
|
||||
const size_t separator = url.find("://");
|
||||
if (separator == std::string::npos) {
|
||||
// Relative URL.
|
||||
return base + url;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}();
|
||||
|
||||
const size_t separator = absoluteURL.find("://");
|
||||
const std::string protocol = separator != std::string::npos ? absoluteURL.substr(0, separator) : "";
|
||||
|
||||
if (protocol == "file") {
|
||||
// load from disk
|
||||
const std::string path = absoluteURL.substr(separator + 3);
|
||||
std::ifstream file(path);
|
||||
|
||||
platform::Response response(callback);
|
||||
if (!file.good()) {
|
||||
response.error_message = "file not found (" + path + ")";
|
||||
} else {
|
||||
std::stringstream data;
|
||||
data << file.rdbuf();
|
||||
response.code = 200;
|
||||
response.body = data.str();
|
||||
}
|
||||
|
||||
callback(&response);
|
||||
} else {
|
||||
// load from the internet
|
||||
platform::request_http(absoluteURL, callback, loop);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2818
styles/bright/img/sprite.json
Normal file
2818
styles/bright/img/sprite.json
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user