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:
Mike Morris
2014-08-11 12:42:49 -04:00
139 changed files with 9598 additions and 4594 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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`.
![edit scheme](https://cloud.githubusercontent.com/assets/98601/3647749/30f74f26-1102-11e4-84af-f1be853b4e38.png)
![setting access token in Xcode scheme](https://cloud.githubusercontent.com/assets/52399/3543326/9e7cfbb8-0860-11e4-8def-3684a9028b61.png)
## OS X

View File

@@ -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

View File

@@ -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'
},

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -14,7 +14,7 @@ public:
double longitude = 0;
double latitude = 0;
double zoom = 0;
double angle = 0;
double bearing = 0;
bool debug = false;
};

View File

@@ -14,7 +14,7 @@ public:
double longitude = 0;
double latitude = 0;
double zoom = 0;
double angle = 0;
double bearing = 0;
bool debug = false;
};

View File

@@ -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];
}

View File

@@ -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);
};
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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;
};
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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 &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;
};
}

View File

@@ -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 {

View File

@@ -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 &map;
public:
const SourceInfo &source;
// Request-related information.
const std::string url;
protected:
std::weak_ptr<platform::Request> req;
std::string data;

View File

@@ -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;
};
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;
};
}

View File

@@ -68,6 +68,7 @@ constexpr EventSeverity disabledEventSeverities[] = {
constexpr Event disabledEvents[] = {
Event(-1) // Avoid zero size array
};
constexpr EventPermutation disabledEventPermutations[] = {

View File

@@ -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

View File

@@ -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);

View 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

View File

@@ -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

View File

@@ -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;
};
}

View File

@@ -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;
};

View File

@@ -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,

View File

@@ -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;

View File

@@ -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();
};

View File

@@ -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>

View File

@@ -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,

View File

@@ -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

View File

@@ -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" },
});
}

View File

@@ -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;
};
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
};
}

View File

@@ -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();

View 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

View File

@@ -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; }
};
}

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -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();

View File

@@ -53,7 +53,7 @@
],
'dependencies': [
'../mapboxgl.gyp:mapboxgl',
'../mapboxgl.gyp:copy_default_stylesheet',
'../mapboxgl.gyp:copy_styles',
'../mapboxgl.gyp:copy_certificate_bundle',
],
},

View File

@@ -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>

View File

@@ -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();

View File

@@ -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',
]
}
]

View File

@@ -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': [

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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(),

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View 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);
}
}
}

View File

@@ -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);
}
}

View 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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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>();
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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.

View File

@@ -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; }

View File

@@ -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());
}
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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
View 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);
}
}
}

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