Merge branch 'master' into pattern-functions
Conflicts: src/mbgl/renderer/painter.cpp src/mbgl/renderer/painter.hpp src/mbgl/renderer/painter_fill.cpp src/mbgl/renderer/painter_line.cpp src/mbgl/style/style_layer.cpp src/mbgl/style/style_layer.hpp src/mbgl/style/style_layer_group.cpp src/mbgl/style/style_layer_group.hpp
This commit is contained in:
@@ -80,7 +80,7 @@ protected:
|
||||
}
|
||||
}
|
||||
pos += itemSize;
|
||||
return static_cast<char *>(array) + (pos - itemSize);
|
||||
return reinterpret_cast<char *>(array) + (pos - itemSize);
|
||||
}
|
||||
|
||||
// Get a pointer to the item at a given index.
|
||||
@@ -92,7 +92,7 @@ protected:
|
||||
if (i * itemSize >= pos) {
|
||||
throw new std::runtime_error("Can't get element after array bounds");
|
||||
} else {
|
||||
return static_cast<char *>(array) + (i * itemSize);
|
||||
return reinterpret_cast<char *>(array) + (i * itemSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,6 @@ struct ElementGroup : public util::noncopyable {
|
||||
: vertex_length(vertex_length_),
|
||||
elements_length(elements_length_) {
|
||||
}
|
||||
|
||||
ElementGroup(ElementGroup &&rhs) noexcept
|
||||
: array(std::move(rhs.array)),
|
||||
vertex_length(rhs.vertex_length),
|
||||
elements_length(rhs.elements_length) {};
|
||||
};
|
||||
|
||||
class TriangleElementsBuffer : public Buffer<
|
||||
|
||||
@@ -66,13 +66,29 @@ bool SpriteAtlas::resize(const float newRatio) {
|
||||
}
|
||||
|
||||
void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y,
|
||||
uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y,
|
||||
const int width, const int height) {
|
||||
src += src_y * src_stride + src_x;
|
||||
dst += dst_y * dst_stride + dst_x;
|
||||
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[x] = src[x];
|
||||
uint32_t *dst, const int dst_stride, const int dst_height, const int dst_x, const int dst_y,
|
||||
const int width, const int height, const bool wrap) {
|
||||
if (wrap) {
|
||||
|
||||
for (int y = -1; y <= height; y++) {
|
||||
int dst_y_wrapped = (y + dst_y + dst_height) % dst_height;
|
||||
int src_y_wrapped = ((y + height) % height) + src_y;
|
||||
int srcI = src_y_wrapped * src_stride + src_x;
|
||||
int dstI = dst_y_wrapped * dst_stride;
|
||||
for (int x = -1; x <= width; x++) {
|
||||
int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride;
|
||||
int src_x_wrapped = (x + width) % width;
|
||||
dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dst += dst_y * dst_stride + dst_x;
|
||||
src += src_y * src_stride + src_x;
|
||||
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[x] = src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +113,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) {
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name, const bool wrap) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
auto rect_it = images.find(name);
|
||||
@@ -120,7 +136,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) {
|
||||
|
||||
images.emplace(name, rect);
|
||||
|
||||
copy(rect, pos);
|
||||
copy(rect, pos, wrap);
|
||||
|
||||
return rect;
|
||||
}
|
||||
@@ -128,7 +144,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) {
|
||||
SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
Rect<dimension> rect = getImage(name);
|
||||
Rect<dimension> rect = getImage(name, repeating);
|
||||
if (repeating) {
|
||||
// When the image is repeating, get the correct position of the image, rather than the
|
||||
// one rounded up to 4 pixels.
|
||||
@@ -157,7 +173,7 @@ void SpriteAtlas::allocate() {
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) {
|
||||
void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) {
|
||||
if (!sprite->raster) return;
|
||||
const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
|
||||
if (!src_img) return;
|
||||
@@ -171,10 +187,12 @@ void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) {
|
||||
/* source y */ src.y,
|
||||
/* dest buffer */ dst_img,
|
||||
/* dest stride */ width * pixelRatio,
|
||||
/* dest height */ height * pixelRatio,
|
||||
/* dest x */ dst.x * pixelRatio,
|
||||
/* dest y */ dst.y * pixelRatio,
|
||||
/* icon dimension */ src.width,
|
||||
/* icon dimension */ src.height
|
||||
/* icon dimension */ src.height,
|
||||
/* wrap padding */ wrap
|
||||
);
|
||||
|
||||
dirty = true;
|
||||
@@ -188,7 +206,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
|
||||
if (!sprite->isLoaded()) return;
|
||||
|
||||
util::erase_if(uninitialized, [this](const std::string &name) {
|
||||
Rect<dimension> dst = getImage(name);
|
||||
Rect<dimension> dst = getImage(name, false);
|
||||
const SpritePosition& src = sprite->getSpritePosition(name);
|
||||
if (!src) {
|
||||
if (debug::spriteWarnings) {
|
||||
@@ -198,7 +216,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
|
||||
}
|
||||
|
||||
if (src.width == dst.w * pixelRatio && src.height == dst.h * pixelRatio && src.pixelRatio == pixelRatio) {
|
||||
copy(dst, src);
|
||||
copy(dst, src, false);
|
||||
return true;
|
||||
} else {
|
||||
if (debug::spriteWarnings) {
|
||||
@@ -217,8 +235,8 @@ void SpriteAtlas::bind(bool linear) {
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
#endif
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
|
||||
first = true;
|
||||
} else {
|
||||
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
// 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);
|
||||
Rect<dimension> getImage(const std::string& name, const bool wrap);
|
||||
|
||||
SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false);
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
private:
|
||||
void allocate();
|
||||
Rect<SpriteAtlas::dimension> allocateImage(size_t width, size_t height);
|
||||
void copy(const Rect<dimension>& dst, const SpritePosition& src);
|
||||
void copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap);
|
||||
|
||||
std::recursive_mutex mtx;
|
||||
float pixelRatio = 1.0f;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
VertexArrayObject::VertexArrayObject() {
|
||||
}
|
||||
|
||||
VertexArrayObject::~VertexArrayObject() {
|
||||
if (!gl::DeleteVertexArrays) return;
|
||||
|
||||
|
||||
@@ -11,15 +11,8 @@ namespace mbgl {
|
||||
|
||||
class VertexArrayObject : public util::noncopyable {
|
||||
public:
|
||||
inline VertexArrayObject() {};
|
||||
|
||||
inline VertexArrayObject(VertexArrayObject &&rhs) noexcept
|
||||
: vao(rhs.vao),
|
||||
bound_shader(rhs.bound_shader),
|
||||
bound_shader_name(rhs.bound_shader_name),
|
||||
bound_vertex_buffer(rhs.bound_vertex_buffer),
|
||||
bound_elements_buffer(rhs.bound_elements_buffer),
|
||||
bound_offset(rhs.bound_offset) {};
|
||||
VertexArrayObject();
|
||||
~VertexArrayObject();
|
||||
|
||||
template <typename Shader, typename VertexBuffer>
|
||||
inline void bind(Shader& shader, VertexBuffer &vertexBuffer, char *offset) {
|
||||
@@ -50,7 +43,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~VertexArrayObject();
|
||||
inline GLuint getID() const {
|
||||
return vao;
|
||||
}
|
||||
|
||||
private:
|
||||
void bindVertexArrayObject();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/util/transition.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/clip_ids.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -25,6 +24,7 @@
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -54,44 +54,12 @@ const static bool uvVersionCheck = []() {
|
||||
return true;
|
||||
}();
|
||||
|
||||
|
||||
#include <zlib.h>
|
||||
// Check zlib library version.
|
||||
const static bool zlibVersionCheck = []() {
|
||||
const char *const version = zlibVersion();
|
||||
if (version[0] != ZLIB_VERSION[0]) {
|
||||
throw std::runtime_error(mbgl::util::sprintf<96>(
|
||||
"zlib version mismatch: headers report %s, but library reports %s", ZLIB_VERSION, version));
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
// Check sqlite3 library version.
|
||||
const static bool sqliteVersionCheck = []() {
|
||||
if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) {
|
||||
throw std::runtime_error(mbgl::util::sprintf<96>(
|
||||
"sqlite3 libversion mismatch: headers report %d, but library reports %d",
|
||||
SQLITE_VERSION_NUMBER, sqlite3_libversion_number()));
|
||||
}
|
||||
if (strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) != 0) {
|
||||
throw std::runtime_error(mbgl::util::sprintf<256>(
|
||||
"sqlite3 sourceid mismatch: headers report \"%s\", but library reports \"%s\"",
|
||||
SQLITE_SOURCE_ID, sqlite3_sourceid()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
Map::Map(View& view_, FileSource& fileSource_)
|
||||
: loop(util::make_unique<uv::loop>()),
|
||||
view(view_),
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
mainThread(std::this_thread::get_id()),
|
||||
mapThread(mainThread),
|
||||
#endif
|
||||
@@ -128,9 +96,7 @@ Map::~Map() {
|
||||
}
|
||||
|
||||
uv::worker &Map::getWorker() {
|
||||
if (!workers) {
|
||||
workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker");
|
||||
}
|
||||
assert(workers);
|
||||
return *workers;
|
||||
}
|
||||
|
||||
@@ -154,8 +120,6 @@ void Map::start(bool startPaused) {
|
||||
workers.reset();
|
||||
activeSources.clear();
|
||||
|
||||
fileSource.clearLoop();
|
||||
|
||||
terminating = true;
|
||||
|
||||
// Closes all open handles on the loop. This means that the loop will automatically terminate.
|
||||
@@ -188,7 +152,7 @@ void Map::start(bool startPaused) {
|
||||
}
|
||||
|
||||
thread = std::thread([this]() {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
mapThread = std::this_thread::get_id();
|
||||
#endif
|
||||
|
||||
@@ -198,7 +162,7 @@ void Map::start(bool startPaused) {
|
||||
|
||||
run();
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
mapThread = std::thread::id();
|
||||
#endif
|
||||
|
||||
@@ -266,7 +230,7 @@ void Map::resume() {
|
||||
|
||||
void Map::run() {
|
||||
if (mode == Mode::None) {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
mapThread = mainThread;
|
||||
#endif
|
||||
mode = Mode::Static;
|
||||
@@ -278,6 +242,9 @@ void Map::run() {
|
||||
}
|
||||
|
||||
view.activate();
|
||||
|
||||
workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker");
|
||||
|
||||
setup();
|
||||
prepare();
|
||||
|
||||
@@ -298,11 +265,10 @@ void Map::run() {
|
||||
// *after* all events have been processed.
|
||||
if (mode == Mode::Static) {
|
||||
render();
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
mapThread = std::thread::id();
|
||||
#endif
|
||||
mode = Mode::None;
|
||||
fileSource.clearLoop();
|
||||
}
|
||||
|
||||
view.deactivate();
|
||||
@@ -387,13 +353,14 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
|
||||
style = std::make_shared<Style>();
|
||||
}
|
||||
|
||||
style->base = base;
|
||||
style->loadJSON((const uint8_t *)styleJSON.c_str());
|
||||
style->cascadeClasses(classes);
|
||||
fileSource.setBase(base);
|
||||
glyphStore->setURL(style->glyph_url);
|
||||
|
||||
style->setDefaultTransitionDuration(defaultTransitionDuration);
|
||||
|
||||
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
|
||||
glyphStore->setURL(glyphURL);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -435,13 +402,13 @@ void Map::cancelTransitions() {
|
||||
|
||||
#pragma mark - Position
|
||||
|
||||
void Map::moveBy(double dx, double dy, double duration) {
|
||||
transform.moveBy(dx, dy, duration * 1_second);
|
||||
void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration duration) {
|
||||
transform.moveBy(dx, dy, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::setLonLat(double lon, double lat, double duration) {
|
||||
transform.setLonLat(lon, lat, duration * 1_second);
|
||||
void Map::setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration) {
|
||||
transform.setLonLat(lon, lat, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -469,13 +436,13 @@ void Map::resetPosition() {
|
||||
|
||||
#pragma mark - Scale
|
||||
|
||||
void Map::scaleBy(double ds, double cx, double cy, double duration) {
|
||||
transform.scaleBy(ds, cx, cy, duration * 1_second);
|
||||
void Map::scaleBy(double ds, double cx, double cy, std::chrono::steady_clock::duration duration) {
|
||||
transform.scaleBy(ds, cx, cy, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::setScale(double scale, double cx, double cy, double duration) {
|
||||
transform.setScale(scale, cx, cy, duration * 1_second);
|
||||
void Map::setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration) {
|
||||
transform.setScale(scale, cx, cy, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -483,8 +450,8 @@ double Map::getScale() const {
|
||||
return transform.getScale();
|
||||
}
|
||||
|
||||
void Map::setZoom(double zoom, double duration) {
|
||||
transform.setZoom(zoom, duration * 1_second);
|
||||
void Map::setZoom(double zoom, std::chrono::steady_clock::duration duration) {
|
||||
transform.setZoom(zoom, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -492,8 +459,8 @@ double Map::getZoom() const {
|
||||
return transform.getZoom();
|
||||
}
|
||||
|
||||
void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) {
|
||||
transform.setLonLatZoom(lon, lat, zoom, duration * 1_second);
|
||||
void Map::setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration) {
|
||||
transform.setLonLatZoom(lon, lat, zoom, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -526,13 +493,13 @@ double Map::getMaxZoom() const {
|
||||
|
||||
#pragma mark - Rotation
|
||||
|
||||
void Map::rotateBy(double sx, double sy, double ex, double ey, double duration) {
|
||||
transform.rotateBy(sx, sy, ex, ey, duration * 1_second);
|
||||
void Map::rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration) {
|
||||
transform.rotateBy(sx, sy, ex, ey, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::setBearing(double degrees, double duration) {
|
||||
transform.setAngle(-degrees * M_PI / 180, duration * 1_second);
|
||||
void Map::setBearing(double degrees, std::chrono::steady_clock::duration duration) {
|
||||
transform.setAngle(-degrees * M_PI / 180, duration);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -546,7 +513,7 @@ double Map::getBearing() const {
|
||||
}
|
||||
|
||||
void Map::resetNorth() {
|
||||
transform.setAngle(0, 500_milliseconds);
|
||||
transform.setAngle(0, std::chrono::milliseconds(500));
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -560,6 +527,15 @@ void Map::stopRotating() {
|
||||
update();
|
||||
}
|
||||
|
||||
#pragma mark - Access Token
|
||||
|
||||
void Map::setAccessToken(const std::string &token) {
|
||||
accessToken = token;
|
||||
}
|
||||
|
||||
const std::string &Map::getAccessToken() const {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
#pragma mark - Toggles
|
||||
|
||||
@@ -618,14 +594,14 @@ std::vector<std::string> Map::getClasses() const {
|
||||
return classes;
|
||||
}
|
||||
|
||||
void Map::setDefaultTransitionDuration(uint64_t milliseconds) {
|
||||
defaultTransitionDuration = milliseconds;
|
||||
void Map::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) {
|
||||
defaultTransitionDuration = duration;
|
||||
if (style) {
|
||||
style->setDefaultTransitionDuration(milliseconds);
|
||||
style->setDefaultTransitionDuration(duration);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Map::getDefaultTransitionDuration() {
|
||||
std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() {
|
||||
return defaultTransitionDuration;
|
||||
}
|
||||
|
||||
@@ -675,20 +651,16 @@ void Map::updateTiles() {
|
||||
source->source->update(*this, getWorker(),
|
||||
style, *glyphAtlas, *glyphStore,
|
||||
*spriteAtlas, getSprite(),
|
||||
*texturePool, fileSource, [this](){ update(); });
|
||||
*texturePool, fileSource, ***loop, [this](){ update(); });
|
||||
}
|
||||
}
|
||||
|
||||
void Map::prepare() {
|
||||
if (!fileSource.hasLoop()) {
|
||||
fileSource.setLoop(**loop);
|
||||
}
|
||||
|
||||
if (!style) {
|
||||
style = std::make_shared<Style>();
|
||||
|
||||
fileSource.request(ResourceType::JSON, styleURL)->onload([&](const Response &res) {
|
||||
if (res.code == 200) {
|
||||
fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
// Calculate the base
|
||||
const size_t pos = styleURL.rfind('/');
|
||||
std::string base = "";
|
||||
@@ -698,20 +670,20 @@ void Map::prepare() {
|
||||
|
||||
setStyleJSON(res.data, base);
|
||||
} else {
|
||||
Log::Error(Event::Setup, "loading style failed: %ld (%s)", res.code, res.message.c_str());
|
||||
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update transform transitions.
|
||||
animationTime = util::now();
|
||||
animationTime = std::chrono::steady_clock::now();
|
||||
if (transform.needsTransition()) {
|
||||
transform.updateTransitions(animationTime);
|
||||
}
|
||||
|
||||
state = transform.currentState();
|
||||
|
||||
animationTime = util::now();
|
||||
animationTime = std::chrono::steady_clock::now();
|
||||
updateSources();
|
||||
style->updateProperties(state.getNormalizedZoom(), animationTime);
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
using namespace mbgl;
|
||||
|
||||
|
||||
RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_)
|
||||
: TileData(id_, source_),
|
||||
RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_)
|
||||
: TileData(id_, source_, fileSource_),
|
||||
bucket(texturePool, properties) {
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class RasterTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&);
|
||||
RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &);
|
||||
~RasterTileData();
|
||||
|
||||
virtual void parse();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/raster_tile_data.hpp>
|
||||
@@ -39,8 +40,9 @@ void Source::load(Map& map, FileSource& fileSource) {
|
||||
|
||||
util::ptr<Source> source = shared_from_this();
|
||||
|
||||
fileSource.request(ResourceType::JSON, info.url)->onload([source, &map](const Response &res) {
|
||||
if (res.code != 200) {
|
||||
const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
|
||||
fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) {
|
||||
if (res.status != Response::Successful) {
|
||||
Log::Warning(Event::General, "failed to load source TileJSON");
|
||||
return;
|
||||
}
|
||||
@@ -155,7 +157,7 @@ TileData::State Source::addTile(Map& map, uv::worker& worker,
|
||||
util::ptr<Style> style,
|
||||
GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
|
||||
SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
|
||||
FileSource& fileSource, TexturePool& texturePool,
|
||||
FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool,
|
||||
const Tile::ID& id,
|
||||
std::function<void ()> callback) {
|
||||
const TileData::State state = hasTile(id);
|
||||
@@ -188,14 +190,14 @@ TileData::State Source::addTile(Map& map, uv::worker& worker,
|
||||
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style,
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite,
|
||||
texturePool, info);
|
||||
texturePool, info, fileSource);
|
||||
} else if (info.type == SourceType::Raster) {
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource);
|
||||
} else {
|
||||
throw std::runtime_error("source type not implemented");
|
||||
}
|
||||
|
||||
new_tile.data->request(worker, fileSource, map.getState().getPixelRatio(), callback);
|
||||
new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback);
|
||||
tile_data.emplace(new_tile.data->id, new_tile.data);
|
||||
}
|
||||
|
||||
@@ -286,7 +288,7 @@ void Source::update(Map& map, uv::worker& worker,
|
||||
util::ptr<Style> style,
|
||||
GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
|
||||
SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
|
||||
TexturePool& texturePool, FileSource& fileSource,
|
||||
TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop,
|
||||
std::function<void ()> callback) {
|
||||
if (!loaded || map.getTime() <= updated)
|
||||
return;
|
||||
@@ -310,7 +312,7 @@ void Source::update(Map& map, uv::worker& worker,
|
||||
const TileData::State state = addTile(map, worker, style,
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite,
|
||||
fileSource, texturePool,
|
||||
fileSource, loop, texturePool,
|
||||
id, callback);
|
||||
|
||||
if (state != TileData::State::parsed) {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
@@ -14,6 +13,7 @@
|
||||
#include <forward_list>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
util::ptr<Style>,
|
||||
GlyphAtlas&, GlyphStore&,
|
||||
SpriteAtlas&, util::ptr<Sprite>,
|
||||
TexturePool&, FileSource&,
|
||||
TexturePool&, FileSource&, uv_loop_t& loop,
|
||||
std::function<void ()> callback);
|
||||
|
||||
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
util::ptr<Style>,
|
||||
GlyphAtlas&, GlyphStore&,
|
||||
SpriteAtlas&, util::ptr<Sprite>,
|
||||
FileSource&, TexturePool&,
|
||||
FileSource&, uv_loop_t &, TexturePool&,
|
||||
const Tile::ID&,
|
||||
std::function<void ()> callback);
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
bool loaded = false;
|
||||
|
||||
// Stores the time when this source was most recently updated.
|
||||
timestamp updated = 0;
|
||||
std::chrono::steady_clock::time_point updated = std::chrono::steady_clock::time_point::min();
|
||||
|
||||
std::map<Tile::ID, std::unique_ptr<Tile>> tiles;
|
||||
std::map<Tile::ID, std::weak_ptr<TileData>> tile_data;
|
||||
|
||||
@@ -52,6 +52,7 @@ Sprite::operator bool() const {
|
||||
// The reason this isn't part of the constructor is that calling shared_from_this() in
|
||||
// the constructor fails.
|
||||
void Sprite::load(FileSource& fileSource) {
|
||||
|
||||
if (!valid) {
|
||||
// Treat a non-existent sprite as a successfully loaded empty sprite.
|
||||
loadedImage = true;
|
||||
@@ -62,26 +63,26 @@ void Sprite::load(FileSource& fileSource) {
|
||||
|
||||
util::ptr<Sprite> sprite = shared_from_this();
|
||||
|
||||
fileSource.request(ResourceType::JSON, jsonURL)->onload([sprite](const Response &res) {
|
||||
if (res.code == 200) {
|
||||
fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
sprite->body = res.data;
|
||||
sprite->parseJSON();
|
||||
sprite->complete();
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res.code, res.message.c_str());
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
|
||||
if (!sprite->future.valid()) {
|
||||
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileSource.request(ResourceType::Image, spriteURL)->onload([sprite](const Response &res) {
|
||||
if (res.code == 200) {
|
||||
fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
sprite->image = res.data;
|
||||
sprite->parseImage();
|
||||
sprite->complete();
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res.code, res.message.c_str());
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %s", res.message.c_str());
|
||||
if (!sprite->future.valid()) {
|
||||
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
|
||||
}
|
||||
@@ -91,7 +92,6 @@ void Sprite::load(FileSource& fileSource) {
|
||||
|
||||
void Sprite::complete() {
|
||||
if (loadedImage && loadedJSON) {
|
||||
Log::Info(Event::Sprite, "loaded %s", spriteURL.c_str());
|
||||
promise.set_value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,29 +6,33 @@
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TileData::TileData(Tile::ID const& id_, const SourceInfo& source_)
|
||||
TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_)
|
||||
: id(id_),
|
||||
name(id),
|
||||
state(State::initial),
|
||||
source(source_),
|
||||
fileSource(fileSource_),
|
||||
debugBucket(debugFontBuffer) {
|
||||
// Initialize tile debug coordinates
|
||||
debugFontBuffer.addText(name.c_str(), 50, 200, 5);
|
||||
}
|
||||
|
||||
TileData::~TileData() {
|
||||
cancel();
|
||||
if (req) {
|
||||
fileSource.cancel(req);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string TileData::toString() const {
|
||||
return std::string { "[tile " } + name + "]";
|
||||
}
|
||||
|
||||
void TileData::request(uv::worker& worker, FileSource& fileSource,
|
||||
float pixelRatio, std::function<void ()> callback) {
|
||||
void TileData::request(uv::worker &worker, uv_loop_t &loop,
|
||||
float pixelRatio, std::function<void()> callback) {
|
||||
if (source.tiles.empty())
|
||||
return;
|
||||
|
||||
@@ -51,8 +55,7 @@ void TileData::request(uv::worker& worker, FileSource& fileSource,
|
||||
|
||||
// Note: Somehow this feels slower than the change to request_http()
|
||||
std::weak_ptr<TileData> weak_tile = shared_from_this();
|
||||
req = fileSource.request(ResourceType::Tile, url);
|
||||
req->onload([weak_tile, url, callback, &worker](const Response &res) {
|
||||
req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) {
|
||||
util::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
|
||||
@@ -61,9 +64,9 @@ void TileData::request(uv::worker& worker, FileSource& fileSource,
|
||||
}
|
||||
|
||||
// Clear the request object.
|
||||
tile->req.reset();
|
||||
tile->req = nullptr;
|
||||
|
||||
if (res.code == 200) {
|
||||
if (res.status == Response::Successful) {
|
||||
tile->state = State::loaded;
|
||||
|
||||
tile->data = res.data;
|
||||
@@ -71,9 +74,7 @@ void TileData::request(uv::worker& worker, FileSource& fileSource,
|
||||
// Schedule tile parsing in another thread
|
||||
tile->reparse(worker, callback);
|
||||
} else {
|
||||
#if defined(DEBUG)
|
||||
fprintf(stderr, "[%s] tile loading failed: %ld, %s\n", url.c_str(), res.code, res.message.c_str());
|
||||
#endif
|
||||
Log::Error(Event::HttpRequest, "[%s] tile loading failed: %s", url.c_str(), res.message.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -81,10 +82,10 @@ void TileData::request(uv::worker& worker, FileSource& fileSource,
|
||||
void TileData::cancel() {
|
||||
if (state != State::obsolete) {
|
||||
state = State::obsolete;
|
||||
if (req) {
|
||||
req->cancel();
|
||||
req.reset();
|
||||
}
|
||||
}
|
||||
if (req) {
|
||||
fileSource.cancel(req);
|
||||
req = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace uv {
|
||||
class worker;
|
||||
}
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
@@ -46,10 +48,10 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
TileData(Tile::ID const& id, const SourceInfo&);
|
||||
TileData(Tile::ID const& id, const SourceInfo&, FileSource&);
|
||||
~TileData();
|
||||
|
||||
void request(uv::worker&, FileSource&, float pixelRatio, std::function<void ()> callback);
|
||||
void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback);
|
||||
void reparse(uv::worker&, std::function<void ()> callback);
|
||||
void cancel();
|
||||
const std::string toString() const;
|
||||
@@ -71,9 +73,10 @@ public:
|
||||
|
||||
public:
|
||||
const SourceInfo& source;
|
||||
FileSource& fileSource;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Request> req;
|
||||
Request *req = nullptr;
|
||||
std::string data;
|
||||
|
||||
// Contains the tile ID string for painting debug information.
|
||||
|
||||
@@ -91,6 +91,100 @@ void TileParser::parseStyleLayers(util::ptr<StyleLayerGroup> group) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct PropertyEvaluator {
|
||||
typedef T result_type;
|
||||
PropertyEvaluator(float z_) : z(z_) {}
|
||||
|
||||
template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
|
||||
T operator()(const P &value) const {
|
||||
return value;
|
||||
}
|
||||
|
||||
T operator()(const Function<T> &value) const {
|
||||
return mapbox::util::apply_visitor(FunctionEvaluator<T>(z), value);
|
||||
}
|
||||
|
||||
template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
|
||||
T operator()(const P &) const {
|
||||
return T();
|
||||
}
|
||||
|
||||
private:
|
||||
const float z;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void TileParser::applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &target, const float z) {
|
||||
auto it = classProperties.properties.find(key);
|
||||
if (it != classProperties.properties.end()) {
|
||||
const PropertyEvaluator<T> evaluator(z);
|
||||
target = mapbox::util::apply_visitor(evaluator, it->second);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void TileParser::applyLayoutProperties<FillProperties>(StyleBucket &bucket_desc, const float) {
|
||||
bucket_desc.render.set<StyleBucketFill>();
|
||||
// no-op; Fill buckets don't currently have any applicable layout properties
|
||||
}
|
||||
|
||||
template<>
|
||||
void TileParser::applyLayoutProperties<LineProperties>(StyleBucket &bucket_desc, const float z) {
|
||||
bucket_desc.render.set<StyleBucketLine>();
|
||||
StyleBucketLine &line = bucket_desc.render.get<StyleBucketLine>();
|
||||
applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, line.cap, z);
|
||||
applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, line.join, z);
|
||||
applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, line.miter_limit, z);
|
||||
applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, line.round_limit, z);
|
||||
}
|
||||
|
||||
template<>
|
||||
void TileParser::applyLayoutProperties<SymbolProperties>(StyleBucket &bucket_desc, const float z) {
|
||||
bucket_desc.render.set<StyleBucketSymbol>();
|
||||
StyleBucketSymbol &symbol = bucket_desc.render.get<StyleBucketSymbol>();
|
||||
applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, symbol.placement, z);
|
||||
if (symbol.placement == PlacementType::Line) {
|
||||
symbol.icon.rotation_alignment = RotationAlignmentType::Map;
|
||||
symbol.text.rotation_alignment = RotationAlignmentType::Map;
|
||||
};
|
||||
applyLayoutProperty(PropertyKey::SymbolMinDistance, bucket_desc.layout, symbol.min_distance, z);
|
||||
applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, symbol.avoid_edges, z);
|
||||
|
||||
applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, symbol.icon.allow_overlap, z);
|
||||
applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, symbol.icon.ignore_placement, z);
|
||||
applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, symbol.icon.optional, z);
|
||||
applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, symbol.icon.rotation_alignment, z);
|
||||
applyLayoutProperty(PropertyKey::IconMaxSize, bucket_desc.layout, symbol.icon.max_size, z);
|
||||
applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, symbol.icon.image, z);
|
||||
applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, symbol.icon.padding, z);
|
||||
applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, symbol.icon.rotate, z);
|
||||
applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, symbol.icon.keep_upright, z);
|
||||
applyLayoutProperty(PropertyKey::IconOffsetX, bucket_desc.layout, symbol.icon.offset[0], z);
|
||||
applyLayoutProperty(PropertyKey::IconOffsetY, bucket_desc.layout, symbol.icon.offset[1], z);
|
||||
|
||||
applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, symbol.text.rotation_alignment, z);
|
||||
applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, symbol.text.field, z);
|
||||
applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, symbol.text.font, z);
|
||||
applyLayoutProperty(PropertyKey::TextMaxSize, bucket_desc.layout, symbol.text.max_size, z);
|
||||
applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, symbol.text.max_width, z);
|
||||
applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, symbol.text.line_height, z);
|
||||
applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, symbol.text.letter_spacing, z);
|
||||
applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, symbol.text.max_angle, z);
|
||||
applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, symbol.text.rotate, z);
|
||||
applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, symbol.text.padding, z);
|
||||
applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, symbol.text.ignore_placement, z);
|
||||
applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, symbol.text.optional, z);
|
||||
applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, symbol.text.justify, z);
|
||||
applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, symbol.text.anchor, z);
|
||||
applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, symbol.text.keep_upright, z);
|
||||
applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, symbol.text.transform, z);
|
||||
applyLayoutProperty(PropertyKey::TextOffsetX, bucket_desc.layout, symbol.text.offset[0], z);
|
||||
applyLayoutProperty(PropertyKey::TextOffsetY, bucket_desc.layout, symbol.text.offset[1], z);
|
||||
applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, symbol.text.allow_overlap, z);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_desc) {
|
||||
if (!bucket_desc) {
|
||||
fprintf(stderr, "missing bucket desc\n");
|
||||
@@ -105,13 +199,16 @@ std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_d
|
||||
auto layer_it = vector_data.layers.find(bucket_desc->source_layer);
|
||||
if (layer_it != vector_data.layers.end()) {
|
||||
const VectorTileLayer &layer = layer_it->second;
|
||||
if (bucket_desc->render.is<StyleBucketFill>()) {
|
||||
if (bucket_desc->type == StyleLayerType::Fill) {
|
||||
applyLayoutProperties<FillProperties>(*bucket_desc, tile.id.z);
|
||||
return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketFill>());
|
||||
} else if (bucket_desc->render.is<StyleBucketLine>()) {
|
||||
} else if (bucket_desc->type == StyleLayerType::Line) {
|
||||
applyLayoutProperties<LineProperties>(*bucket_desc, tile.id.z);
|
||||
return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketLine>());
|
||||
} else if (bucket_desc->render.is<StyleBucketSymbol>()) {
|
||||
} else if (bucket_desc->type == StyleLayerType::Symbol) {
|
||||
applyLayoutProperties<SymbolProperties>(*bucket_desc, tile.id.z);
|
||||
return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketSymbol>());
|
||||
} else if (bucket_desc->render.is<StyleBucketRaster>()) {
|
||||
} else if (bucket_desc->type == StyleLayerType::Raster) {
|
||||
return nullptr;
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucket_desc->name.c_str(), bucket_desc->source_layer.c_str());
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/style/filter_expression.hpp>
|
||||
#include <mbgl/style/class_properties.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
@@ -48,6 +50,10 @@ public:
|
||||
private:
|
||||
bool obsolete() const;
|
||||
void parseStyleLayers(util::ptr<StyleLayerGroup> group);
|
||||
|
||||
template <typename T> void applyLayoutProperties(StyleBucket &bucket, float z);
|
||||
template <typename T> void applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &, float z);
|
||||
|
||||
std::unique_ptr<Bucket> createBucket(util::ptr<StyleBucket> bucket_desc);
|
||||
|
||||
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#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>
|
||||
|
||||
@@ -48,16 +47,16 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio,
|
||||
|
||||
#pragma mark - Position
|
||||
|
||||
void Transform::moveBy(const double dx, const double dy, const timestamp duration) {
|
||||
void Transform::moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
_moveBy(dx, dy, duration);
|
||||
}
|
||||
|
||||
void Transform::_moveBy(const double dx, const double dy, const timestamp duration) {
|
||||
void Transform::_moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) {
|
||||
// This is only called internally, so we don't need a lock here.
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionWillChangeAnimated :
|
||||
MapChangeRegionWillChange);
|
||||
|
||||
@@ -66,25 +65,25 @@ void Transform::_moveBy(const double dx, const double dy, const timestamp durati
|
||||
|
||||
constrain(final.scale, final.y);
|
||||
|
||||
if (duration == 0) {
|
||||
if (duration == std::chrono::steady_clock::duration::zero()) {
|
||||
current.x = final.x;
|
||||
current.y = final.y;
|
||||
} else {
|
||||
// Use a common start time for all of the transitions to avoid divergent transitions.
|
||||
timestamp start = util::now();
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
transitions.emplace_front(
|
||||
std::make_shared<util::ease_transition<double>>(current.x, final.x, current.x, start, duration));
|
||||
transitions.emplace_front(
|
||||
std::make_shared<util::ease_transition<double>>(current.y, final.y, current.y, start, duration));
|
||||
}
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionDidChangeAnimated :
|
||||
MapChangeRegionDidChange,
|
||||
duration);
|
||||
}
|
||||
|
||||
void Transform::setLonLat(const double lon, const double lat, const timestamp duration) {
|
||||
void Transform::setLonLat(const double lon, const double lat, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999);
|
||||
@@ -95,7 +94,7 @@ void Transform::setLonLat(const double lon, const double lat, const timestamp du
|
||||
}
|
||||
|
||||
void Transform::setLonLatZoom(const double lon, const double lat, const double zoom,
|
||||
const timestamp duration) {
|
||||
const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
double new_scale = std::pow(2.0, zoom);
|
||||
@@ -131,8 +130,8 @@ void Transform::startPanning() {
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
current.panning = true;
|
||||
timestamp start = util::now();
|
||||
pan_timeout = std::make_shared<util::timeout<bool>>(false, current.panning, start, 200_milliseconds);
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
pan_timeout = std::make_shared<util::timeout<bool>>(false, current.panning, start, std::chrono::steady_clock::duration(200));
|
||||
transitions.emplace_front(pan_timeout);
|
||||
}
|
||||
|
||||
@@ -152,7 +151,7 @@ void Transform::_clearPanning() {
|
||||
|
||||
#pragma mark - Zoom
|
||||
|
||||
void Transform::scaleBy(const double ds, const double cx, const double cy, const timestamp duration) {
|
||||
void Transform::scaleBy(const double ds, const double cx, const double cy, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
// clamp scale to min/max values
|
||||
@@ -167,13 +166,13 @@ void Transform::scaleBy(const double ds, const double cx, const double cy, const
|
||||
}
|
||||
|
||||
void Transform::setScale(const double scale, const double cx, const double cy,
|
||||
const timestamp duration) {
|
||||
const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
_setScale(scale, cx, cy, duration);
|
||||
}
|
||||
|
||||
void Transform::setZoom(const double zoom, const timestamp duration) {
|
||||
void Transform::setZoom(const double zoom, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
_setScale(std::pow(2.0, zoom), -1, -1, duration);
|
||||
@@ -198,8 +197,8 @@ void Transform::startScaling() {
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
current.scaling = true;
|
||||
timestamp start = util::now();
|
||||
scale_timeout = std::make_shared<util::timeout<bool>>(false, current.scaling, start, 200_milliseconds);
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
scale_timeout = std::make_shared<util::timeout<bool>>(false, current.scaling, start, std::chrono::milliseconds(200));
|
||||
transitions.emplace_front(scale_timeout);
|
||||
}
|
||||
|
||||
@@ -231,7 +230,7 @@ void Transform::_clearScaling() {
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::_setScale(double new_scale, double cx, double cy, const timestamp duration) {
|
||||
void Transform::_setScale(double new_scale, double cx, double cy, const std::chrono::steady_clock::duration duration) {
|
||||
// This is only called internally, so we don't need a lock here.
|
||||
|
||||
// Ensure that we don't zoom in further than the maximum allowed.
|
||||
@@ -265,10 +264,10 @@ void Transform::_setScale(double new_scale, double cx, double cy, const timestam
|
||||
}
|
||||
|
||||
void Transform::_setScaleXY(const double new_scale, const double xn, const double yn,
|
||||
const timestamp duration) {
|
||||
const std::chrono::steady_clock::duration duration) {
|
||||
// This is only called internally, so we don't need a lock here.
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionWillChangeAnimated :
|
||||
MapChangeRegionWillChange);
|
||||
|
||||
@@ -278,13 +277,13 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
|
||||
|
||||
constrain(final.scale, final.y);
|
||||
|
||||
if (duration == 0) {
|
||||
if (duration == std::chrono::steady_clock::duration::zero()) {
|
||||
current.scale = final.scale;
|
||||
current.x = final.x;
|
||||
current.y = final.y;
|
||||
} else {
|
||||
// Use a common start time for all of the transitions to avoid divergent transitions.
|
||||
timestamp start = util::now();
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
|
||||
current.scale, final.scale, current.scale, start, duration));
|
||||
transitions.emplace_front(
|
||||
@@ -297,7 +296,7 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
|
||||
Bc = s / 360;
|
||||
Cc = s / (2 * M_PI);
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionDidChangeAnimated :
|
||||
MapChangeRegionDidChange,
|
||||
duration);
|
||||
@@ -319,7 +318,7 @@ void Transform::constrain(double& scale, double& y) const {
|
||||
#pragma mark - Angle
|
||||
|
||||
void Transform::rotateBy(const double start_x, const double start_y, const double end_x,
|
||||
const double end_y, const timestamp duration) {
|
||||
const double end_y, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
double center_x = current.width / 2, center_y = current.height / 2;
|
||||
@@ -349,7 +348,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
|
||||
_setAngle(ang, duration);
|
||||
}
|
||||
|
||||
void Transform::setAngle(const double new_angle, const timestamp duration) {
|
||||
void Transform::setAngle(const double new_angle, const std::chrono::steady_clock::duration duration) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
_setAngle(new_angle, duration);
|
||||
@@ -363,20 +362,20 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
|
||||
if (cx >= 0 && cy >= 0) {
|
||||
dx = (final.width / 2) - cx;
|
||||
dy = (final.height / 2) - cy;
|
||||
_moveBy(dx, dy, 0);
|
||||
_moveBy(dx, dy, std::chrono::steady_clock::duration::zero());
|
||||
}
|
||||
|
||||
_setAngle(new_angle, 0);
|
||||
_setAngle(new_angle, std::chrono::steady_clock::duration::zero());
|
||||
|
||||
if (cx >= 0 && cy >= 0) {
|
||||
_moveBy(-dx, -dy, 0);
|
||||
_moveBy(-dx, -dy, std::chrono::steady_clock::duration::zero());
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::_setAngle(double new_angle, const timestamp duration) {
|
||||
void Transform::_setAngle(double new_angle, const std::chrono::steady_clock::duration duration) {
|
||||
// This is only called internally, so we don't need a lock here.
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionWillChangeAnimated :
|
||||
MapChangeRegionWillChange);
|
||||
|
||||
@@ -387,15 +386,15 @@ void Transform::_setAngle(double new_angle, const timestamp duration) {
|
||||
|
||||
final.angle = new_angle;
|
||||
|
||||
if (duration == 0) {
|
||||
if (duration == std::chrono::steady_clock::duration::zero()) {
|
||||
current.angle = final.angle;
|
||||
} else {
|
||||
timestamp start = util::now();
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
|
||||
current.angle, final.angle, current.angle, start, duration));
|
||||
}
|
||||
|
||||
view.notifyMapChange(duration ?
|
||||
view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ?
|
||||
MapChangeRegionDidChangeAnimated :
|
||||
MapChangeRegionDidChange,
|
||||
duration);
|
||||
@@ -414,8 +413,8 @@ void Transform::startRotating() {
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
current.rotating = true;
|
||||
timestamp start = util::now();
|
||||
rotate_timeout = std::make_shared<util::timeout<bool>>(false, current.rotating, start, 200_milliseconds);
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
rotate_timeout = std::make_shared<util::timeout<bool>>(false, current.rotating, start, std::chrono::milliseconds(200));
|
||||
transitions.emplace_front(rotate_timeout);
|
||||
}
|
||||
|
||||
@@ -443,7 +442,7 @@ bool Transform::needsTransition() const {
|
||||
return !transitions.empty();
|
||||
}
|
||||
|
||||
void Transform::updateTransitions(const timestamp now) {
|
||||
void Transform::updateTransitions(const std::chrono::steady_clock::time_point now) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
transitions.remove_if([now](const util::ptr<util::transition> &transition) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
@@ -13,8 +14,8 @@ VectorTileData::VectorTileData(Tile::ID const& id_,
|
||||
GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_,
|
||||
SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_,
|
||||
TexturePool& texturePool_,
|
||||
const SourceInfo& source_)
|
||||
: TileData(id_, source_),
|
||||
const SourceInfo& source_, FileSource &fileSource_)
|
||||
: TileData(id_, source_, fileSource_),
|
||||
glyphAtlas(glyphAtlas_),
|
||||
glyphStore(glyphStore_),
|
||||
spriteAtlas(spriteAtlas_),
|
||||
@@ -35,6 +36,10 @@ void VectorTileData::parse() {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!style) {
|
||||
throw std::runtime_error("style isn't present in VectorTileData object anymore");
|
||||
}
|
||||
|
||||
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
||||
// the TileParser object writes results into this objects. All other state
|
||||
// is going to be discarded afterwards.
|
||||
@@ -42,12 +47,13 @@ void VectorTileData::parse() {
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite,
|
||||
texturePool);
|
||||
// Clear the style so that we don't have a cycle in the shared_ptr references.
|
||||
style.reset();
|
||||
|
||||
parser.parse();
|
||||
} catch (const std::exception& ex) {
|
||||
#if defined(DEBUG)
|
||||
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
#endif
|
||||
cancel();
|
||||
Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
|
||||
state = State::obsolete;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
GlyphAtlas&, GlyphStore&,
|
||||
SpriteAtlas&, util::ptr<Sprite>,
|
||||
TexturePool&,
|
||||
const SourceInfo&);
|
||||
const SourceInfo&, FileSource &);
|
||||
~VectorTileData();
|
||||
|
||||
virtual void parse();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
@@ -111,12 +112,13 @@ void FillBucket::tessellate() {
|
||||
throw geometry_too_long_exception();
|
||||
}
|
||||
|
||||
if (!lineGroups.size() || (lineGroups.back().vertex_length + total_vertex_count > 65535)) {
|
||||
if (!lineGroups.size() || (lineGroups.back()->vertex_length + total_vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
lineGroups.emplace_back();
|
||||
lineGroups.emplace_back(util::make_unique<line_group_type>());
|
||||
}
|
||||
|
||||
line_group_type& lineGroup = lineGroups.back();
|
||||
assert(lineGroups.back());
|
||||
line_group_type& lineGroup = *lineGroups.back();
|
||||
uint32_t lineIndex = lineGroup.vertex_length;
|
||||
|
||||
for (const std::vector<ClipperLib::IntPoint>& polygon : polygons) {
|
||||
@@ -157,14 +159,15 @@ void FillBucket::tessellate() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!triangleGroups.size() || (triangleGroups.back().vertex_length + total_vertex_count > 65535)) {
|
||||
if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + total_vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
triangleGroups.emplace_back();
|
||||
triangleGroups.emplace_back(util::make_unique<triangle_group_type>());
|
||||
}
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
// coordinate in this polygon.
|
||||
triangle_group_type& triangleGroup = triangleGroups.back();
|
||||
assert(triangleGroups.back());
|
||||
triangle_group_type& triangleGroup = *triangleGroups.back();
|
||||
uint32_t triangleIndex = triangleGroup.vertex_length;
|
||||
|
||||
for (int i = 0; i < triangle_count; ++i) {
|
||||
@@ -215,32 +218,35 @@ bool FillBucket::hasData() const {
|
||||
void FillBucket::drawElements(PlainShader& 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[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void FillBucket::drawElements(PatternShader& 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[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void FillBucket::drawVertices(OutlineShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(line_elements_start * lineElementsBuffer.itemSize);
|
||||
for (line_group_type& group : lineGroups) {
|
||||
group.array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group.elements_length * 2, GL_UNSIGNED_SHORT, elements_index));
|
||||
vertex_index += group.vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group.elements_length * lineElementsBuffer.itemSize;
|
||||
for (auto& group : lineGroups) {
|
||||
assert(group);
|
||||
group->array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group->elements_length * 2, GL_UNSIGNED_SHORT, elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * lineElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ private:
|
||||
const size_t line_elements_start;
|
||||
VertexArrayObject array;
|
||||
|
||||
std::vector<triangle_group_type> triangleGroups;
|
||||
std::vector<line_group_type> lineGroups;
|
||||
std::vector<std::unique_ptr<triangle_group_type>> triangleGroups;
|
||||
std::vector<std::unique_ptr<line_group_type>> lineGroups;
|
||||
|
||||
std::vector<ClipperLib::IntPoint> line;
|
||||
bool hasVertices = false;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
using namespace mbgl;
|
||||
|
||||
// Record frame history that will be used to calculate fading params
|
||||
void FrameHistory::record(timestamp now, float zoom) {
|
||||
void FrameHistory::record(std::chrono::steady_clock::time_point now, float zoom) {
|
||||
// first frame ever
|
||||
if (!history.size()) {
|
||||
history.emplace_back(FrameSnapshot{0, zoom});
|
||||
history.emplace_back(FrameSnapshot{0, zoom});
|
||||
history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom});
|
||||
history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom});
|
||||
}
|
||||
|
||||
if (history.size() > 0 || history.back().z != zoom) {
|
||||
@@ -15,7 +15,7 @@ void FrameHistory::record(timestamp now, float zoom) {
|
||||
}
|
||||
}
|
||||
|
||||
bool FrameHistory::needsAnimation(const timestamp duration) const {
|
||||
bool FrameHistory::needsAnimation(const std::chrono::steady_clock::duration duration) const {
|
||||
if (!history.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ bool FrameHistory::needsAnimation(const timestamp duration) const {
|
||||
const FrameSnapshot &pivot = history.back();
|
||||
|
||||
int i = -1;
|
||||
while ((int)history.size() > i + 1 && history[i + 1].t + duration < pivot.t) {
|
||||
while ((int)history.size() > i + 1 && history[i + 1].now + duration < pivot.now) {
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -47,16 +47,16 @@ bool FrameHistory::needsAnimation(const timestamp duration) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
FadeProperties FrameHistory::getFadeProperties(timestamp duration)
|
||||
FadeProperties FrameHistory::getFadeProperties(std::chrono::steady_clock::duration duration)
|
||||
{
|
||||
const timestamp currentTime = util::now();
|
||||
const std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now();
|
||||
|
||||
// Remove frames until only one is outside the duration, or until there are only three
|
||||
while (history.size() > 3 && history[1].t + duration < currentTime) {
|
||||
while (history.size() > 3 && history[1].now + duration < currentTime) {
|
||||
history.pop_front();
|
||||
}
|
||||
|
||||
if (history[1].t + duration < currentTime) {
|
||||
if (history[1].now + duration < currentTime) {
|
||||
history[0].z = history[1].z;
|
||||
}
|
||||
|
||||
@@ -69,12 +69,13 @@ FadeProperties FrameHistory::getFadeProperties(timestamp duration)
|
||||
|
||||
// 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 zoomDiff = endingZ - history[1].z;
|
||||
std::chrono::duration<float> timeDiff = lastFrame.now - history[1].now;
|
||||
float fadedist = zoomDiff / (timeDiff / duration);
|
||||
|
||||
// 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;
|
||||
float bump = std::chrono::duration<float>(currentTime - lastFrame.now) / duration * fadedist;
|
||||
|
||||
return FadeProperties {
|
||||
fadedist,
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
#include <deque>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct FrameSnapshot {
|
||||
explicit inline FrameSnapshot(timestamp t_, float z_) : t(t_), z(z_) {}
|
||||
float t;
|
||||
explicit inline FrameSnapshot(std::chrono::steady_clock::time_point now_, float z_) : now(now_), z(z_) {}
|
||||
std::chrono::steady_clock::time_point now;
|
||||
float z;
|
||||
};
|
||||
|
||||
@@ -26,10 +26,10 @@ struct FadeProperties {
|
||||
class FrameHistory {
|
||||
public:
|
||||
// Record frame history that will be used to calculate fading params
|
||||
void record(timestamp now, float zoom);
|
||||
void record(std::chrono::steady_clock::time_point now, float zoom);
|
||||
|
||||
bool needsAnimation(timestamp duration) const;
|
||||
FadeProperties getFadeProperties(timestamp duration);
|
||||
bool needsAnimation(std::chrono::steady_clock::duration duration) const;
|
||||
FadeProperties getFadeProperties(std::chrono::steady_clock::duration duration);
|
||||
|
||||
public:
|
||||
std::deque<FrameSnapshot> history;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
@@ -306,12 +307,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
|
||||
// Store the triangle/line groups.
|
||||
{
|
||||
if (!triangleGroups.size() || (triangleGroups.back().vertex_length + vertex_count > 65535)) {
|
||||
if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
triangleGroups.emplace_back();
|
||||
triangleGroups.emplace_back(util::make_unique<triangle_group_type>());
|
||||
}
|
||||
|
||||
triangle_group_type& group = triangleGroups.back();
|
||||
assert(triangleGroups.back());
|
||||
triangle_group_type& group = *triangleGroups.back();
|
||||
for (const TriangleElement& triangle : triangle_store) {
|
||||
triangleElementsBuffer.add(
|
||||
group.vertex_length + triangle.a,
|
||||
@@ -326,12 +328,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
|
||||
// Store the line join/cap groups.
|
||||
{
|
||||
if (!pointGroups.size() || (pointGroups.back().vertex_length + vertex_count > 65535)) {
|
||||
if (!pointGroups.size() || (pointGroups.back()->vertex_length + vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
pointGroups.emplace_back();
|
||||
pointGroups.emplace_back(util::make_unique<point_group_type>());
|
||||
}
|
||||
|
||||
point_group_type& group = pointGroups.back();
|
||||
assert(pointGroups.back());
|
||||
point_group_type& group = *pointGroups.back();
|
||||
for (PointElement point : point_store) {
|
||||
pointElementsBuffer.add(group.vertex_length + point);
|
||||
}
|
||||
@@ -351,8 +354,9 @@ bool LineBucket::hasData() const {
|
||||
|
||||
bool LineBucket::hasPoints() const {
|
||||
if (!pointGroups.empty()) {
|
||||
for (const point_group_type& group : pointGroups) {
|
||||
if (group.elements_length) {
|
||||
for (const auto& group : pointGroups) {
|
||||
assert(group);
|
||||
if (group->elements_length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -363,55 +367,59 @@ bool LineBucket::hasPoints() const {
|
||||
void LineBucket::drawLines(LineShader& 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) {
|
||||
if (!group.elements_length) {
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group.array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawLineSDF(LineSDFShader& 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) {
|
||||
if (!group.elements_length) {
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group.array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawLinePatterns(LinepatternShader& 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) {
|
||||
if (!group.elements_length) {
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group.array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawPoints(LinejoinShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize);
|
||||
for (point_group_type& group : pointGroups) {
|
||||
if (!group.elements_length) {
|
||||
for (auto& group : pointGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group.array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group.elements_length, GL_UNSIGNED_SHORT, elements_index));
|
||||
vertex_index += group.vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group.elements_length * pointElementsBuffer.itemSize;
|
||||
group->array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group->elements_length, GL_UNSIGNED_SHORT, elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * pointElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ private:
|
||||
const size_t triangle_elements_start;
|
||||
const size_t point_elements_start;
|
||||
|
||||
std::vector<triangle_group_type> triangleGroups;
|
||||
std::vector<point_group_type> pointGroups;
|
||||
std::vector<std::unique_ptr<triangle_group_type>> triangleGroups;
|
||||
std::vector<std::unique_ptr<point_group_type>> pointGroups;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/clip_ids.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/mat3.hpp>
|
||||
@@ -35,7 +34,7 @@ Painter::~Painter() {
|
||||
}
|
||||
|
||||
bool Painter::needsAnimation() const {
|
||||
return frameHistory.needsAnimation(300);
|
||||
return frameHistory.needsAnimation(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
void Painter::setup() {
|
||||
@@ -216,7 +215,7 @@ void Painter::prepareTile(const Tile& tile) {
|
||||
}
|
||||
|
||||
void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>& sources,
|
||||
TransformState state_, timestamp time) {
|
||||
TransformState state_, std::chrono::steady_clock::time_point time) {
|
||||
state = state_;
|
||||
|
||||
clear();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
void render(const Style& style,
|
||||
const std::set<util::ptr<StyleSource>>& sources,
|
||||
TransformState state,
|
||||
timestamp time);
|
||||
std::chrono::steady_clock::time_point time);
|
||||
|
||||
void renderLayers(util::ptr<StyleLayerGroup> group);
|
||||
void renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace mbgl;
|
||||
@@ -52,7 +53,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
|
||||
sdfShader.u_angle = (int32_t)(angle + 256) % 256;
|
||||
sdfShader.u_zoom = (state.getNormalizedZoom() - zoomAdjust) * 10; // current zoom level
|
||||
|
||||
FadeProperties f = frameHistory.getFadeProperties(300_milliseconds);
|
||||
FadeProperties f = frameHistory.getFadeProperties(std::chrono::milliseconds(300));
|
||||
sdfShader.u_fadedist = f.fadedist * 10;
|
||||
sdfShader.u_minfadezoom = std::floor(f.minfadezoom * 10);
|
||||
sdfShader.u_maxfadezoom = std::floor(f.maxfadezoom * 10);
|
||||
|
||||
@@ -177,13 +177,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
|
||||
if (feature.label.length()) {
|
||||
shaping = fontStack.getShaping(
|
||||
/* string */ feature.label,
|
||||
/* maxWidth */ properties.text.max_width,
|
||||
/* lineHeight */ properties.text.line_height,
|
||||
/* maxWidth: ems */ properties.text.max_width * 24,
|
||||
/* lineHeight: ems */ properties.text.line_height * 24,
|
||||
/* horizontalAlign */ horizontalAlign,
|
||||
/* verticalAlign */ verticalAlign,
|
||||
/* justify */ justify,
|
||||
/* spacing */ properties.text.letter_spacing,
|
||||
/* translate */ properties.text.offset);
|
||||
/* spacing: ems */ properties.text.letter_spacing * 24,
|
||||
/* translate */ vec2<float>(properties.text.offset[0], properties.text.offset[1]));
|
||||
|
||||
// Add the glyphs we need for this label to the glyph atlas.
|
||||
if (shaping.size()) {
|
||||
@@ -195,7 +195,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
|
||||
// if feature has icon, get sprite atlas position
|
||||
if (feature.sprite.length()) {
|
||||
sprite.waitUntilLoaded();
|
||||
image = spriteAtlas.getImage(feature.sprite);
|
||||
image = spriteAtlas.getImage(feature.sprite, false);
|
||||
|
||||
if (sprite.getSpritePosition(feature.sprite).sdf) {
|
||||
sdfIcons = true;
|
||||
@@ -322,19 +322,19 @@ void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping
|
||||
collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange,
|
||||
horizontalText);
|
||||
}
|
||||
if (inside) addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange);
|
||||
if (inside) addSymbols<TextBuffer, TextElementGroup>(text, glyphPlacement.shapes, glyphScale, glyphRange);
|
||||
}
|
||||
|
||||
if (iconScale && std::isfinite(iconScale)) {
|
||||
if (!properties.icon.ignore_placement) {
|
||||
collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon);
|
||||
}
|
||||
if (inside) addSymbols(icon, iconPlacement.shapes, iconScale, iconRange);
|
||||
if (inside) addSymbols<IconBuffer, IconElementGroup>(icon, iconPlacement.shapes, iconScale, iconRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
template <typename Buffer, typename GroupType>
|
||||
void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale,
|
||||
PlacementRange placementRange) {
|
||||
const float zoom = collision.zoom;
|
||||
@@ -366,14 +366,15 @@ void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float
|
||||
const int glyph_vertex_length = 4;
|
||||
|
||||
if (!buffer.groups.size() ||
|
||||
(buffer.groups.back().vertex_length + glyph_vertex_length > 65535)) {
|
||||
(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();
|
||||
buffer.groups.emplace_back(util::make_unique<GroupType>());
|
||||
}
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
// coordinate in this polygon.
|
||||
auto &triangleGroup = buffer.groups.back();
|
||||
assert(buffer.groups.back());
|
||||
auto &triangleGroup = *buffer.groups.back();
|
||||
uint32_t triangleIndex = triangleGroup.vertex_length;
|
||||
|
||||
// coordinates (2 triangles)
|
||||
@@ -398,33 +399,36 @@ void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float
|
||||
void SymbolBucket::drawGlyphs(SDFShader &shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(0);
|
||||
char *elements_index = BUFFER_OFFSET(0);
|
||||
for (TextElementGroup &group : text.groups) {
|
||||
group.array[0].bind(shader, text.vertices, text.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
for (auto &group : text.groups) {
|
||||
assert(group);
|
||||
group->array[0].bind(shader, text.vertices, text.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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(SDFShader &shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(0);
|
||||
char *elements_index = BUFFER_OFFSET(0);
|
||||
for (IconElementGroup &group : icon.groups) {
|
||||
group.array[0].bind(shader, icon.vertices, icon.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
for (auto &group : icon.groups) {
|
||||
assert(group);
|
||||
group->array[0].bind(shader, icon.vertices, icon.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolBucket::drawIcons(IconShader &shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(0);
|
||||
char *elements_index = BUFFER_OFFSET(0);
|
||||
for (IconElementGroup &group : icon.groups) {
|
||||
group.array[1].bind(shader, icon.vertices, icon.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
for (auto &group : icon.groups) {
|
||||
assert(group);
|
||||
group->array[1].bind(shader, icon.vertices, icon.triangles, vertex_index);
|
||||
MBGL_CHECK_ERROR(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ private:
|
||||
|
||||
|
||||
// Adds placed items to the buffer.
|
||||
template <typename Buffer>
|
||||
template <typename Buffer, typename GroupType>
|
||||
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.
|
||||
@@ -95,16 +95,16 @@ public:
|
||||
private:
|
||||
Collision &collision;
|
||||
|
||||
struct {
|
||||
struct TextBuffer {
|
||||
TextVertexBuffer vertices;
|
||||
TriangleElementsBuffer triangles;
|
||||
std::vector<TextElementGroup> groups;
|
||||
std::vector<std::unique_ptr<TextElementGroup>> groups;
|
||||
} text;
|
||||
|
||||
struct {
|
||||
struct IconBuffer {
|
||||
IconVertexBuffer vertices;
|
||||
TriangleElementsBuffer triangles;
|
||||
std::vector<IconElementGroup> groups;
|
||||
std::vector<std::unique_ptr<IconElementGroup>> groups;
|
||||
} icon;
|
||||
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef MBGL_STORAGE_ASSET_REQUEST
|
||||
#define MBGL_STORAGE_ASSET_REQUEST
|
||||
|
||||
#include <mbgl/storage/base_request.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
struct AssetRequestBaton;
|
||||
|
||||
class AssetRequest : public BaseRequest {
|
||||
public:
|
||||
AssetRequest(const std::string &path, uv_loop_t *loop);
|
||||
~AssetRequest();
|
||||
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
AssetRequestBaton *ptr = nullptr;
|
||||
|
||||
friend struct AssetRequestBaton;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
#include <mbgl/storage/base_request.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/storage/request.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
void invoke(const std::forward_list<std::unique_ptr<Callback>> &list, Args&& ...args) {
|
||||
for (const std::unique_ptr<Callback> &callback : list) {
|
||||
assert(callback);
|
||||
if (callback->is<T>()) {
|
||||
callback->get<T>()(::std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseRequest::BaseRequest(const std::string &path_) : threadId(std::this_thread::get_id()), path(path_) {
|
||||
}
|
||||
|
||||
// A base request can only be "canceled" by destroying the object. In that case, we'll have to
|
||||
// notify all cancel callbacks.
|
||||
BaseRequest::~BaseRequest() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
notify();
|
||||
}
|
||||
|
||||
void BaseRequest::retryImmediately() {
|
||||
// no-op. override in child class.
|
||||
}
|
||||
|
||||
void BaseRequest::notify() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
|
||||
// The parameter exists solely so that any calls to ->remove()
|
||||
// are not going to cause deallocation of this object while this call is in progress.
|
||||
util::ptr<BaseRequest> retain = self;
|
||||
|
||||
// Swap the lists so that it's safe for callbacks to call ->cancel()
|
||||
// on the request object, which would modify the list.
|
||||
const std::forward_list<std::unique_ptr<Callback>> list = std::move(callbacks);
|
||||
callbacks.clear();
|
||||
|
||||
if (response) {
|
||||
invoke<CompletedCallback>(list, *response);
|
||||
} else {
|
||||
invoke<AbortedCallback>(list);
|
||||
}
|
||||
|
||||
self.reset();
|
||||
}
|
||||
|
||||
Callback *BaseRequest::add(Callback &&callback, const util::ptr<BaseRequest> &request) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
assert(this == request.get());
|
||||
|
||||
if (response) {
|
||||
// We already have a response. Notify right away.
|
||||
if (callback.is<CompletedCallback>()) {
|
||||
callback.get<CompletedCallback>()(*response);
|
||||
} else {
|
||||
// We already know that this request was successful. The AbortedCallback will be discarded
|
||||
// here since it would never be called.
|
||||
}
|
||||
return nullptr;
|
||||
} else {
|
||||
self = request;
|
||||
callbacks.push_front(util::make_unique<Callback>(std::move(callback)));
|
||||
return callbacks.front().get();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRequest::remove(Callback *callback) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
callbacks.remove_if([=](const std::unique_ptr<Callback> &cb) {
|
||||
return cb.get() == callback;
|
||||
});
|
||||
if (callbacks.empty()) {
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef MBGL_STORAGE_BASE_REQUEST
|
||||
#define MBGL_STORAGE_BASE_REQUEST
|
||||
|
||||
#include <mbgl/storage/request_callback.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <forward_list>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
typedef struct uv_async_s uv_async_t;
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Response;
|
||||
class Request;
|
||||
|
||||
class BaseRequest {
|
||||
private:
|
||||
// Make noncopyable and immovable
|
||||
BaseRequest(const BaseRequest &) = delete;
|
||||
BaseRequest(BaseRequest &&) = delete;
|
||||
BaseRequest& operator=(const BaseRequest &) = delete;
|
||||
BaseRequest& operator=(BaseRequest &&) = delete;
|
||||
|
||||
public:
|
||||
BaseRequest(const std::string &path);
|
||||
virtual ~BaseRequest();
|
||||
|
||||
Callback *add(Callback &&callback, const util::ptr<BaseRequest> &request);
|
||||
void remove(Callback *callback);
|
||||
|
||||
// Must be called by subclasses when a valid Response object is available. It will notify
|
||||
// all listeners.
|
||||
void notify();
|
||||
|
||||
// This function is called when the request ought to be stopped. Any subclass must make sure this
|
||||
// is also called in its destructor. Calling this function repeatedly must be safe.
|
||||
// This function must call notify().
|
||||
virtual void cancel() = 0;
|
||||
|
||||
// This function is called when the request should be reattempted immediately. This is typically
|
||||
// reaction to a network status change.
|
||||
virtual void retryImmediately();
|
||||
|
||||
public:
|
||||
const std::thread::id threadId;
|
||||
const std::string path;
|
||||
std::unique_ptr<Response> response;
|
||||
|
||||
protected:
|
||||
// This object may hold a shared_ptr to itself. It does this to prevent destruction of this object
|
||||
// while a request is in progress.
|
||||
util::ptr<BaseRequest> self;
|
||||
std::forward_list<std::unique_ptr<Callback>> callbacks;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,136 +0,0 @@
|
||||
#include <mbgl/storage/caching_http_file_source.hpp>
|
||||
#include <mbgl/storage/asset_request.hpp>
|
||||
#include <mbgl/storage/http_request.hpp>
|
||||
#include <mbgl/storage/sqlite_store.hpp>
|
||||
#include <mbgl/storage/asset_request.hpp>
|
||||
#include <mbgl/util/uv-messenger.h>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
CachingHTTPFileSource::CachingHTTPFileSource(const std::string &path_)
|
||||
: path(path_) {}
|
||||
|
||||
CachingHTTPFileSource::~CachingHTTPFileSource() {
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::setLoop(uv_loop_t* loop_) {
|
||||
assert(!loop);
|
||||
|
||||
threadId = std::this_thread::get_id();
|
||||
store = !path.empty() ? util::ptr<SQLiteStore>(new SQLiteStore(loop_, path)) : nullptr;
|
||||
loop = loop_;
|
||||
queue = new uv_messenger_t;
|
||||
|
||||
uv_messenger_init(loop, queue, [](void *ptr) {
|
||||
std::unique_ptr<std::function<void()>> fn { reinterpret_cast<std::function<void()> *>(ptr) };
|
||||
(*fn)();
|
||||
});
|
||||
uv_unref((uv_handle_t *)&queue->async);
|
||||
}
|
||||
|
||||
bool CachingHTTPFileSource::hasLoop() {
|
||||
return loop;
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::clearLoop() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
assert(loop);
|
||||
|
||||
uv_messenger_stop(queue, [](uv_messenger_t *msgr) {
|
||||
delete msgr;
|
||||
});
|
||||
|
||||
util::ptr<BaseRequest> req;
|
||||
|
||||
// Send a cancel() message to all requests that we are still holding.
|
||||
for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
|
||||
if ((req = pair.second.lock())) {
|
||||
req->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
store.reset();
|
||||
|
||||
loop = nullptr;
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::setBase(std::string value) {
|
||||
// TODO: Make threadsafe.
|
||||
base.swap(value);
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::setAccessToken(std::string value) {
|
||||
// TODO: Make threadsafe.
|
||||
accessToken.swap(value);
|
||||
}
|
||||
|
||||
std::string CachingHTTPFileSource::getAccessToken() const {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
std::unique_ptr<Request> CachingHTTPFileSource::request(ResourceType type, const std::string& url_) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
|
||||
std::string url = url_;
|
||||
|
||||
// Make URL absolute.
|
||||
const size_t separator = url.find("://");
|
||||
if (separator == std::string::npos) {
|
||||
url = base + url;
|
||||
}
|
||||
|
||||
// Normalize mapbox:// URLs.
|
||||
switch (type) {
|
||||
case ResourceType::Glyphs:
|
||||
url = util::mapbox::normalizeGlyphsURL(url, accessToken);
|
||||
default:
|
||||
url = util::mapbox::normalizeSourceURL(url, accessToken);
|
||||
}
|
||||
|
||||
util::ptr<BaseRequest> req;
|
||||
|
||||
// First, try to find an existing Request object.
|
||||
auto it = pending.find(url);
|
||||
if (it != pending.end()) {
|
||||
req = it->second.lock();
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
if (url.substr(0, 8) == "asset://") {
|
||||
req = std::make_shared<AssetRequest>(url.substr(8), loop);
|
||||
} else {
|
||||
req = std::make_shared<HTTPRequest>(type, url, loop, store);
|
||||
}
|
||||
|
||||
pending.emplace(url, req);
|
||||
}
|
||||
|
||||
return util::make_unique<Request>(req);
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::prepare(std::function<void()> fn) {
|
||||
if (std::this_thread::get_id() == threadId) {
|
||||
fn();
|
||||
} else {
|
||||
uv_messenger_send(queue, new std::function<void()>(std::move(fn)));
|
||||
}
|
||||
}
|
||||
|
||||
void CachingHTTPFileSource::setReachability(bool reachable) {
|
||||
if (reachable && loop) {
|
||||
prepare([this]() {
|
||||
util::ptr<BaseRequest> req;
|
||||
for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
|
||||
if ((req = pair.second.lock())) {
|
||||
req->retryImmediately();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
245
src/mbgl/storage/default_file_source.cpp
Normal file
245
src/mbgl/storage/default_file_source.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/storage/default/request.hpp>
|
||||
#include <mbgl/storage/default/asset_request.hpp>
|
||||
#include <mbgl/storage/default/http_request.hpp>
|
||||
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/util/async_queue.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#ifndef __clang__
|
||||
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace algo = boost::algorithm;
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct DefaultFileSource::ActionDispatcher {
|
||||
DefaultFileSource &fileSource;
|
||||
template <typename T> void operator()(T &t) { fileSource.process(t); }
|
||||
};
|
||||
|
||||
struct DefaultFileSource::AddRequestAction {
|
||||
Request *const request;
|
||||
};
|
||||
|
||||
struct DefaultFileSource::RemoveRequestAction {
|
||||
Request *const request;
|
||||
};
|
||||
|
||||
struct DefaultFileSource::ResultAction {
|
||||
const Resource resource;
|
||||
std::unique_ptr<Response> response;
|
||||
};
|
||||
|
||||
struct DefaultFileSource::StopAction {
|
||||
};
|
||||
|
||||
|
||||
DefaultFileSource::DefaultFileSource(FileCache *cache_)
|
||||
: loop(uv_loop_new()),
|
||||
cache(cache_),
|
||||
queue(new Queue(loop, [this](Action &action) {
|
||||
mapbox::util::apply_visitor(ActionDispatcher{*this}, action);
|
||||
})),
|
||||
thread([this]() {
|
||||
#ifdef __APPLE__
|
||||
pthread_setname_np("FileSource");
|
||||
#endif
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
}) {
|
||||
}
|
||||
|
||||
DefaultFileSource::DefaultFileSource(FileCache *cache_, uv_loop_t *loop_)
|
||||
: loop(loop_),
|
||||
cache(cache_),
|
||||
queue(new Queue(loop, [this](Action &action) {
|
||||
mapbox::util::apply_visitor(ActionDispatcher{*this}, action);
|
||||
})) {
|
||||
// Make sure that the queue doesn't block the loop from exiting.
|
||||
queue->unref();
|
||||
}
|
||||
|
||||
DefaultFileSource::~DefaultFileSource() {
|
||||
MBGL_VERIFY_THREAD(tid);
|
||||
|
||||
if (thread.joinable()) {
|
||||
if (queue) {
|
||||
queue->send(StopAction{ });
|
||||
}
|
||||
thread.join();
|
||||
uv_loop_delete(loop);
|
||||
} else {
|
||||
// Assume that the loop we received is running in the current thread.
|
||||
StopAction action {};
|
||||
process(action);
|
||||
}
|
||||
}
|
||||
|
||||
SharedRequestBase *DefaultFileSource::find(const Resource &resource) {
|
||||
// We're using a set of pointers here instead of a map between url and SharedRequestBase because
|
||||
// we need to find the requests both by pointer and by URL. Given that the number of requests
|
||||
// is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead
|
||||
// anyway.
|
||||
const auto it = pending.find(resource);
|
||||
if (it != pending.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) {
|
||||
auto req = new Request(resource, l, std::move(callback));
|
||||
|
||||
// This function can be called from any thread. Make sure we're executing the actual call in the
|
||||
// file source loop by sending it over the queue. It will be processed in processAction().
|
||||
queue->send(AddRequestAction{ req });
|
||||
return req;
|
||||
}
|
||||
|
||||
void DefaultFileSource::request(const Resource &resource, Callback callback) {
|
||||
auto req = new Request(resource, nullptr, std::move(callback));
|
||||
|
||||
// This function can be called from any thread. Make sure we're executing the actual call in the
|
||||
// file source loop by sending it over the queue. It will be processed in processAction().
|
||||
queue->send(AddRequestAction{ req });
|
||||
}
|
||||
|
||||
void DefaultFileSource::cancel(Request *req) {
|
||||
req->cancel();
|
||||
|
||||
// This function can be called from any thread. Make sure we're executing the actual call in the
|
||||
// file source loop by sending it over the queue. It will be processed in processAction().
|
||||
queue->send(RemoveRequestAction{ req });
|
||||
}
|
||||
|
||||
void DefaultFileSource::process(AddRequestAction &action) {
|
||||
const Resource &resource = action.request->resource;
|
||||
|
||||
// We're adding a new Request.
|
||||
SharedRequestBase *sharedRequest = find(resource);
|
||||
if (!sharedRequest) {
|
||||
// There is no request for this URL yet. Create a new one and start it.
|
||||
if (algo::starts_with(resource.url, "asset://")) {
|
||||
sharedRequest = new AssetRequest(this, resource);
|
||||
} else {
|
||||
sharedRequest = new HTTPRequest(this, resource);
|
||||
}
|
||||
|
||||
// Make sure the loop stays alive when we're not running the file source in it's own thread.
|
||||
if (!thread.joinable() && pending.empty()) {
|
||||
queue->ref();
|
||||
}
|
||||
|
||||
const bool inserted = pending.emplace(resource, sharedRequest).second;
|
||||
assert(inserted);
|
||||
(void (inserted)); // silence unused variable warning on Release builds.
|
||||
|
||||
// But first, we're going to start querying the database if it exists.
|
||||
if (!cache) {
|
||||
sharedRequest->start(loop);
|
||||
} else {
|
||||
// Otherwise, first check the cache for existing data so that we can potentially
|
||||
// revalidate the information without having to redownload everything.
|
||||
cache->get(resource, [this, resource](std::unique_ptr<Response> response) {
|
||||
queue->send(ResultAction { resource, std::move(response) });
|
||||
});
|
||||
}
|
||||
}
|
||||
sharedRequest->subscribe(action.request);
|
||||
}
|
||||
|
||||
void DefaultFileSource::process(RemoveRequestAction &action) {
|
||||
SharedRequestBase *sharedRequest = find(action.request->resource);
|
||||
if (sharedRequest) {
|
||||
// If the number of dependent requests of the SharedRequestBase drops to zero, the
|
||||
// unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list
|
||||
// of pending requests and initiates cancelation.
|
||||
sharedRequest->unsubscribe(action.request);
|
||||
} else {
|
||||
// There is no request for this URL anymore. Likely, the request already completed
|
||||
// before we got around to process the cancelation request.
|
||||
}
|
||||
|
||||
// Send a message back to the requesting thread and notify it that this request has been
|
||||
// canceled and is now safe to be deleted.
|
||||
action.request->destruct();
|
||||
}
|
||||
|
||||
void DefaultFileSource::process(ResultAction &action) {
|
||||
SharedRequestBase *sharedRequest = find(action.resource);
|
||||
if (sharedRequest) {
|
||||
if (action.response) {
|
||||
// This entry was stored in the cache. Now determine if we need to revalidate.
|
||||
const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
if (action.response->expires > now) {
|
||||
// The response is fresh. We're good to notify the caller.
|
||||
sharedRequest->notify(std::move(action.response), FileCache::Hint::No);
|
||||
sharedRequest->cancel();
|
||||
return;
|
||||
} else {
|
||||
// The cached response is stale. Now run the real request.
|
||||
sharedRequest->start(loop, std::move(action.response));
|
||||
}
|
||||
} else {
|
||||
// There is no response. Now run the real request.
|
||||
sharedRequest->start(loop);
|
||||
}
|
||||
} else {
|
||||
// There is no request for this URL anymore. Likely, the request was canceled
|
||||
// before we got around to process the cache result.
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultFileSource::process(StopAction &) {
|
||||
// Cancel all remaining requests.
|
||||
for (auto it : pending) {
|
||||
it.second->unsubscribeAll();
|
||||
}
|
||||
pending.clear();
|
||||
|
||||
assert(queue);
|
||||
queue->stop();
|
||||
queue = nullptr;
|
||||
}
|
||||
|
||||
void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
|
||||
const std::set<Request *> &observers,
|
||||
std::shared_ptr<const Response> response, FileCache::Hint hint) {
|
||||
// First, remove the request, since it might be destructed at any point now.
|
||||
assert(find(sharedRequest->resource) == sharedRequest);
|
||||
pending.erase(sharedRequest->resource);
|
||||
|
||||
if (response) {
|
||||
if (cache) {
|
||||
// Store response in database
|
||||
cache->put(sharedRequest->resource, response, hint);
|
||||
}
|
||||
|
||||
// Notify all observers.
|
||||
for (auto it : observers) {
|
||||
it->notify(response);
|
||||
}
|
||||
}
|
||||
|
||||
if (!thread.joinable() && pending.empty()) {
|
||||
// When there are no pending requests, we're going to allow the queue to stop.
|
||||
queue->unref();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
#include <mbgl/storage/http_request.hpp>
|
||||
#include <mbgl/storage/sqlite_store.hpp>
|
||||
#include <mbgl/storage/http_request_baton.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
|
||||
struct CacheRequestBaton {
|
||||
HTTPRequest *request = nullptr;
|
||||
std::string path;
|
||||
util::ptr<SQLiteStore> store;
|
||||
};
|
||||
|
||||
HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path_, uv_loop_t *loop_, util::ptr<SQLiteStore> store_)
|
||||
: BaseRequest(path_), threadId(std::this_thread::get_id()), loop(loop_), store(store_), type(type_) {
|
||||
if (store) {
|
||||
startCacheRequest();
|
||||
} else {
|
||||
startHTTPRequest(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::startCacheRequest() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
|
||||
cacheBaton = new CacheRequestBaton;
|
||||
cacheBaton->request = this;
|
||||
cacheBaton->path = path;
|
||||
cacheBaton->store = store;
|
||||
store->get(path, [](std::unique_ptr<Response> &&response_, void *ptr) {
|
||||
// Wrap in a unique_ptr, so it'll always get auto-destructed.
|
||||
std::unique_ptr<CacheRequestBaton> baton((CacheRequestBaton *)ptr);
|
||||
if (baton->request) {
|
||||
baton->request->cacheBaton = nullptr;
|
||||
baton->request->handleCacheResponse(std::move(response_));
|
||||
}
|
||||
}, cacheBaton);
|
||||
}
|
||||
|
||||
void HTTPRequest::handleCacheResponse(std::unique_ptr<Response> &&res) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
|
||||
if (res) {
|
||||
// This entry was stored in the cache. Now determine if we need to revalidate.
|
||||
const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
if (res->expires > now) {
|
||||
response = std::move(res);
|
||||
notify();
|
||||
// Note: after calling notify(), the request object may cease to exist.
|
||||
// This HTTPRequest is completed.
|
||||
return;
|
||||
} else {
|
||||
// TODO: notify with preliminary results.
|
||||
}
|
||||
}
|
||||
|
||||
startHTTPRequest(std::move(res));
|
||||
}
|
||||
|
||||
void HTTPRequest::startHTTPRequest(std::unique_ptr<Response> &&res) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
assert(!httpBaton);
|
||||
|
||||
httpBaton = std::make_shared<HTTPRequestBaton>(path);
|
||||
httpBaton->request = this;
|
||||
httpBaton->async = new uv_async_t;
|
||||
httpBaton->response = std::move(res);
|
||||
httpBaton->async->data = new util::ptr<HTTPRequestBaton>(httpBaton);
|
||||
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
uv_async_init(loop, httpBaton->async, [](uv_async_t *async, int) {
|
||||
#else
|
||||
uv_async_init(loop, httpBaton->async, [](uv_async_t *async) {
|
||||
#endif
|
||||
util::ptr<HTTPRequestBaton> &baton = *(util::ptr<HTTPRequestBaton> *)async->data;
|
||||
|
||||
if (baton->request) {
|
||||
HTTPRequest *request = baton->request;
|
||||
request->httpBaton.reset();
|
||||
baton->request = nullptr;
|
||||
request->handleHTTPResponse(baton->type, std::move(baton->response));
|
||||
}
|
||||
|
||||
delete (util::ptr<HTTPRequestBaton> *)async->data;
|
||||
uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
|
||||
uv_async_t *async_handle = (uv_async_t *)handle;
|
||||
delete async_handle;
|
||||
});
|
||||
});
|
||||
attempts++;
|
||||
HTTPRequestBaton::start(httpBaton);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HTTPRequest::handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&res) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
assert(!httpBaton);
|
||||
assert(!response);
|
||||
|
||||
switch (responseType) {
|
||||
// This error was caused by a temporary error and it is likely that it will be resolved
|
||||
// immediately. We are going to try again right away. This is like the TemporaryError,
|
||||
// except that we will not perform exponential back-off.
|
||||
case HTTPResponseType::SingularError:
|
||||
if (attempts >= 4) {
|
||||
// Report as error after 4 attempts.
|
||||
response = std::move(res);
|
||||
notify();
|
||||
} else if (attempts >= 2) {
|
||||
// Switch to the back-off algorithm after the second failure.
|
||||
retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
|
||||
return;
|
||||
} else {
|
||||
startHTTPRequest(std::move(res));
|
||||
}
|
||||
break;
|
||||
|
||||
// This error might be resolved by waiting some time (e.g. server issues).
|
||||
// We are going to do an exponential back-off and will try again in a few seconds.
|
||||
case HTTPResponseType::TemporaryError:
|
||||
if (attempts >= 4) {
|
||||
// Report error back after it failed completely.
|
||||
response = std::move(res);
|
||||
notify();
|
||||
} else {
|
||||
retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
|
||||
}
|
||||
break;
|
||||
|
||||
// This error might be resolved once the network reachability status changes.
|
||||
// We are going to watch the network status for changes and will retry as soon as the
|
||||
// operating system notifies us of a network status change.
|
||||
case HTTPResponseType::ConnectionError:
|
||||
|
||||
if (attempts >= 4) {
|
||||
// Report error back after it failed completely.
|
||||
response = std::move(res);
|
||||
notify();
|
||||
} else {
|
||||
// By default, we will retry every 60 seconds.
|
||||
retryHTTPRequest(std::move(res), 60000);
|
||||
}
|
||||
break;
|
||||
|
||||
// The request was canceled programatically.
|
||||
case HTTPResponseType::Canceled:
|
||||
response.reset();
|
||||
notify();
|
||||
break;
|
||||
|
||||
// This error probably won't be resolved by retrying anytime soon. We are giving up.
|
||||
case HTTPResponseType::PermanentError:
|
||||
response = std::move(res);
|
||||
notify();
|
||||
break;
|
||||
|
||||
// The request returned data successfully. We retrieved and decoded the data successfully.
|
||||
case HTTPResponseType::Successful:
|
||||
if (store) {
|
||||
store->put(path, type, *res);
|
||||
}
|
||||
response = std::move(res);
|
||||
notify();
|
||||
break;
|
||||
|
||||
// The request confirmed that the data wasn't changed. We already have the data.
|
||||
case HTTPResponseType::NotModified:
|
||||
if (store) {
|
||||
store->updateExpiration(path, res->expires);
|
||||
}
|
||||
response = std::move(res);
|
||||
notify();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!"Response wasn't set");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using RetryBaton = std::pair<HTTPRequest *, std::unique_ptr<Response>>;
|
||||
|
||||
void HTTPRequest::retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout) {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
assert(!backoffTimer);
|
||||
backoffTimer = new uv_timer_t();
|
||||
uv_timer_init(loop, backoffTimer);
|
||||
backoffTimer->data = new RetryBaton(this, std::move(res));
|
||||
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
uv_timer_start(backoffTimer, [](uv_timer_t *timer, int) {
|
||||
#else
|
||||
uv_timer_start(backoffTimer, [](uv_timer_t *timer) {
|
||||
#endif
|
||||
std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(timer->data) };
|
||||
pair->first->startHTTPRequest(std::move(pair->second));
|
||||
pair->first->backoffTimer = nullptr;
|
||||
uv_timer_stop(timer);
|
||||
uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
|
||||
}, timeout, 0);
|
||||
}
|
||||
|
||||
void HTTPRequest::removeHTTPBaton() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
if (httpBaton) {
|
||||
httpBaton->request = nullptr;
|
||||
HTTPRequestBaton::stop(httpBaton);
|
||||
httpBaton.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::removeCacheBaton() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
if (cacheBaton) {
|
||||
// Make sre that this object doesn't accidentally get accessed when it is destructed before
|
||||
// the callback returned. They are being run in the same thread, so just setting it to
|
||||
// null is sufficient.
|
||||
// Note: We don't manually delete the CacheRequestBaton since it'll be deleted by the
|
||||
// callback.
|
||||
cacheBaton->request = nullptr;
|
||||
cacheBaton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::removeBackoffTimer() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
if (backoffTimer) {
|
||||
delete static_cast<RetryBaton *>(backoffTimer->data);
|
||||
uv_timer_stop(backoffTimer);
|
||||
uv_close((uv_handle_t *)backoffTimer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
|
||||
backoffTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::retryImmediately() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
if (!cacheBaton && !httpBaton) {
|
||||
if (backoffTimer) {
|
||||
// Retry immediately.
|
||||
uv_timer_stop(backoffTimer);
|
||||
std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(backoffTimer->data) };
|
||||
assert(pair->first == this);
|
||||
startHTTPRequest(std::move(pair->second));
|
||||
uv_close((uv_handle_t *)backoffTimer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
|
||||
backoffTimer = nullptr;
|
||||
} else {
|
||||
assert(!"We should always have a backoffTimer when there are no batons");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::cancel() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
removeCacheBaton();
|
||||
removeHTTPBaton();
|
||||
removeBackoffTimer();
|
||||
notify();
|
||||
}
|
||||
|
||||
|
||||
HTTPRequest::~HTTPRequest() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
cancel();
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef MBGL_STORAGE_HTTP_REQUEST
|
||||
#define MBGL_STORAGE_HTTP_REQUEST
|
||||
|
||||
#include <mbgl/storage/resource_type.hpp>
|
||||
#include <mbgl/storage/base_request.hpp>
|
||||
#include <mbgl/storage/http_request_baton.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
typedef struct uv_timer_s uv_timer_t;
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct CacheRequestBaton;
|
||||
struct HTTPRequestBaton;
|
||||
struct CacheEntry;
|
||||
class SQLiteStore;
|
||||
|
||||
class HTTPRequest : public BaseRequest {
|
||||
public:
|
||||
HTTPRequest(ResourceType type, const std::string &path, uv_loop_t *loop, util::ptr<SQLiteStore> store);
|
||||
~HTTPRequest();
|
||||
|
||||
void cancel();
|
||||
void retryImmediately();
|
||||
|
||||
private:
|
||||
void startCacheRequest();
|
||||
void handleCacheResponse(std::unique_ptr<Response> &&response);
|
||||
void startHTTPRequest(std::unique_ptr<Response> &&res);
|
||||
void handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&response);
|
||||
|
||||
void retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout);
|
||||
|
||||
void removeCacheBaton();
|
||||
void removeHTTPBaton();
|
||||
void removeBackoffTimer();
|
||||
|
||||
private:
|
||||
const std::thread::id threadId;
|
||||
uv_loop_t *const loop;
|
||||
CacheRequestBaton *cacheBaton = nullptr;
|
||||
util::ptr<HTTPRequestBaton> httpBaton;
|
||||
uv_timer_t *backoffTimer = nullptr;
|
||||
util::ptr<SQLiteStore> store;
|
||||
const ResourceType type;
|
||||
uint8_t attempts = 0;
|
||||
|
||||
friend struct HTTPRequestBaton;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <mbgl/storage/http_request_baton.hpp>
|
||||
#include <uv.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
HTTPRequestBaton::HTTPRequestBaton(const std::string &path_) : threadId(std::this_thread::get_id()), path(path_) {
|
||||
}
|
||||
|
||||
HTTPRequestBaton::~HTTPRequestBaton() {
|
||||
}
|
||||
|
||||
}
|
||||
32
src/mbgl/storage/network_status.cpp
Normal file
32
src/mbgl/storage/network_status.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <mbgl/storage/network_status.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
// Example: Allocate a reachability object
|
||||
// Reachability* reach = [Reachability reachabilityForInternetConnection];
|
||||
// reach.reachableBlock = ^(Reachability* reach) { NetworkStatus::Reachable(); };
|
||||
// [reach startNotifier];
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
std::mutex NetworkStatus::mtx;
|
||||
std::set<uv_async_t *> NetworkStatus::observers;
|
||||
|
||||
void NetworkStatus::Subscribe(uv_async_t *async) {
|
||||
std::lock_guard<std::mutex> lock(NetworkStatus::mtx);
|
||||
observers.insert(async);
|
||||
}
|
||||
|
||||
void NetworkStatus::Unsubscribe(uv_async_t *async) {
|
||||
std::lock_guard<std::mutex> lock(NetworkStatus::mtx);
|
||||
observers.erase(async);
|
||||
}
|
||||
|
||||
void NetworkStatus::Reachable() {
|
||||
std::lock_guard<std::mutex> lock(NetworkStatus::mtx);
|
||||
for (auto async : observers) {
|
||||
uv_async_send(async);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +1,99 @@
|
||||
#include <mbgl/storage/request.hpp>
|
||||
#include <mbgl/storage/base_request.hpp>
|
||||
#include <mbgl/storage/default/request.hpp>
|
||||
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
Request::Request(const util::ptr<BaseRequest> &base_)
|
||||
: thread_id(std::this_thread::get_id()), base(base_) {
|
||||
}
|
||||
|
||||
Request::~Request() {
|
||||
assert(thread_id == std::this_thread::get_id());
|
||||
}
|
||||
|
||||
void Request::onload(CompletedCallback cb) {
|
||||
assert(thread_id == std::this_thread::get_id());
|
||||
if (base) {
|
||||
Callback *callback = base->add(std::move(cb), base);
|
||||
if (callback) {
|
||||
callbacks.push_front(callback);
|
||||
}
|
||||
// Note: This requires that loop is running in the current thread (or not yet running).
|
||||
Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
|
||||
: callback(callback_), resource(resource_) {
|
||||
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
|
||||
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
|
||||
if (loop) {
|
||||
notify_async = new uv_async_t;
|
||||
notify_async->data = this;
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
uv_async_init(loop, notify_async, [](uv_async_t *async, int) { notifyCallback(async); });
|
||||
#else
|
||||
uv_async_init(loop, notify_async, notifyCallback);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Request::oncancel(AbortedCallback cb) {
|
||||
assert(thread_id == std::this_thread::get_id());
|
||||
if (base) {
|
||||
Callback *callback = base->add(std::move(cb), base);
|
||||
if (callback) {
|
||||
callbacks.push_front(callback);
|
||||
}
|
||||
void Request::notifyCallback(uv_async_t *async) {
|
||||
auto request = reinterpret_cast<Request *>(async->data);
|
||||
uv::close(async);
|
||||
|
||||
if (!request->destruct_async) {
|
||||
// We haven't created a cancel request, so we can safely delete this Request object
|
||||
// since it won't be accessed in the future.
|
||||
assert(request->response);
|
||||
request->callback(*request->response);
|
||||
delete request;
|
||||
} else {
|
||||
// Otherwise, we're waiting for for the destruct notification to be delivered in order
|
||||
// to delete the Request object. We're doing this since we can't know whether the
|
||||
// DefaultFileSource is still sending a cancel event, which means this object must still
|
||||
// exist.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Request::~Request() {
|
||||
if (notify_async) {
|
||||
// Request objects can be destructed in other threads when the user didn't supply a loop.
|
||||
MBGL_VERIFY_THREAD(tid)
|
||||
}
|
||||
}
|
||||
|
||||
void Request::notify(const std::shared_ptr<const Response> &response_) {
|
||||
response = response_;
|
||||
if (notify_async) {
|
||||
uv_async_send(notify_async);
|
||||
} else {
|
||||
assert(response);
|
||||
callback(*response);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void Request::cancel() {
|
||||
assert(thread_id == std::this_thread::get_id());
|
||||
if (base) {
|
||||
for (Callback *callback : callbacks) {
|
||||
base->remove(callback);
|
||||
}
|
||||
base.reset();
|
||||
MBGL_VERIFY_THREAD(tid)
|
||||
assert(notify_async);
|
||||
assert(!destruct_async);
|
||||
destruct_async = new uv_async_t;
|
||||
destruct_async->data = this;
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
uv_async_init(notify_async->loop, destruct_async, [](uv_async_t *async, int) { cancelCallback(async); });
|
||||
#else
|
||||
uv_async_init(notify_async->loop, destruct_async, cancelCallback);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Request::cancelCallback(uv_async_t *async) {
|
||||
// The destruct_async will be invoked *after* the notify_async callback has already run.
|
||||
auto request = reinterpret_cast<Request *>(async->data);
|
||||
uv::close(async);
|
||||
delete request;
|
||||
}
|
||||
|
||||
// This gets called from the FileSource thread, and will only ever be invoked after cancel() was called
|
||||
// in the original requesting thread.
|
||||
void Request::destruct() {
|
||||
if (notify_async) {
|
||||
notify(nullptr);
|
||||
}
|
||||
callbacks.clear();
|
||||
|
||||
assert(destruct_async);
|
||||
uv_async_send(destruct_async);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
int64_t Response::parseCacheControl(const char *value) {
|
||||
if (value) {
|
||||
unsigned long long seconds = 0;
|
||||
// TODO: cache-control may contain other information as well:
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
if (std::sscanf(value, "max-age=%llu", &seconds) == 1) {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count() +
|
||||
seconds;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
#include <mbgl/storage/sqlite_store.hpp>
|
||||
#include <mbgl/util/compression.hpp>
|
||||
#include <mbgl/util/sqlite3.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <mbgl/util/uv-worker.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace mapbox::sqlite;
|
||||
|
||||
std::string removeAccessTokenFromURL(const std::string &url) {
|
||||
const size_t token_start = url.find("access_token=");
|
||||
// Ensure that token exists, isn't at the front and is preceded by either & or ?.
|
||||
if (token_start == std::string::npos || token_start == 0 || !(url[token_start - 1] == '&' || url[token_start - 1] == '?')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const size_t token_end = url.find_first_of('&', token_start);
|
||||
if (token_end == std::string::npos) {
|
||||
// The token is the last query argument. We slice away the "&access_token=..." part
|
||||
return url.substr(0, token_start - 1);
|
||||
} else {
|
||||
// We slice away the "access_token=...&" part.
|
||||
return url.substr(0, token_start) + url.substr(token_end + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string convertMapboxDomainsToProtocol(const std::string &url) {
|
||||
const size_t protocol_separator = url.find("://");
|
||||
if (protocol_separator == std::string::npos) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const std::string protocol = url.substr(0, protocol_separator);
|
||||
if (!(protocol == "http" || protocol == "https")) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const size_t domain_begin = protocol_separator + 3;
|
||||
const size_t path_separator = url.find("/", domain_begin);
|
||||
if (path_separator == std::string::npos) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const std::string domain = url.substr(domain_begin, path_separator - domain_begin);
|
||||
if (domain.find(".tiles.mapbox.com") != std::string::npos) {
|
||||
return "mapbox://" + url.substr(path_separator + 1);
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
std::string unifyMapboxURLs(const std::string &url) {
|
||||
return removeAccessTokenFromURL(convertMapboxDomainsToProtocol(url));
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
SQLiteStore::SQLiteStore(uv_loop_t *loop, const std::string &path)
|
||||
: thread_id(std::this_thread::get_id()),
|
||||
db(std::make_shared<Database>(path.c_str(), ReadWrite | Create)) {
|
||||
createSchema();
|
||||
worker = new uv_worker_t;
|
||||
uv_worker_init(worker, loop, 1, "SQLite");
|
||||
}
|
||||
|
||||
SQLiteStore::~SQLiteStore() {
|
||||
// Nothing to do. This function needs to be here because we're forward-declaring
|
||||
// Database, so we need the actual definition here to be able to properly destruct it.
|
||||
if (worker) {
|
||||
uv_worker_close(worker, [](uv_worker_t *worker_handle) {
|
||||
delete worker_handle;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SQLiteStore::createSchema() {
|
||||
if (!db || !*db) {
|
||||
return;
|
||||
}
|
||||
|
||||
db->exec("CREATE TABLE IF NOT EXISTS `http_cache` ("
|
||||
" `url` TEXT PRIMARY KEY NOT NULL,"
|
||||
" `code` INTEGER NOT NULL,"
|
||||
" `type` INTEGER NOT NULL,"
|
||||
" `modified` INTEGER,"
|
||||
" `etag` TEXT,"
|
||||
" `expires` INTEGER,"
|
||||
" `data` BLOB,"
|
||||
" `compressed` INTEGER NOT NULL DEFAULT 0"
|
||||
");"
|
||||
"CREATE INDEX IF NOT EXISTS `http_cache_type_idx` ON `http_cache` (`type`);");
|
||||
}
|
||||
|
||||
struct GetBaton {
|
||||
util::ptr<Database> db;
|
||||
std::string path;
|
||||
ResourceType type;
|
||||
void *ptr = nullptr;
|
||||
SQLiteStore::GetCallback callback = nullptr;
|
||||
std::unique_ptr<Response> response;
|
||||
};
|
||||
|
||||
void SQLiteStore::get(const std::string &path, GetCallback callback, void *ptr) {
|
||||
assert(std::this_thread::get_id() == thread_id);
|
||||
if (!db || !*db) {
|
||||
if (callback) {
|
||||
callback(nullptr, ptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GetBaton *get_baton = new GetBaton;
|
||||
get_baton->db = db;
|
||||
get_baton->path = path;
|
||||
get_baton->ptr = ptr;
|
||||
get_baton->callback = callback;
|
||||
|
||||
uv_worker_send(worker, get_baton, [](void *data) {
|
||||
GetBaton *baton = (GetBaton *)data;
|
||||
const std::string url = unifyMapboxURLs(baton->path);
|
||||
// 0 1 2
|
||||
Statement stmt = baton->db->prepare("SELECT `code`, `type`, `modified`, "
|
||||
// 3 4 5 6
|
||||
"`etag`, `expires`, `data`, `compressed` FROM `http_cache` WHERE `url` = ?");
|
||||
|
||||
stmt.bind(1, url.c_str());
|
||||
if (stmt.run()) {
|
||||
// There is data.
|
||||
baton->response = util::make_unique<Response>();
|
||||
|
||||
baton->response->code = stmt.get<int>(0);
|
||||
baton->type = ResourceType(stmt.get<int>(1));
|
||||
baton->response->modified = stmt.get<int64_t>(2);
|
||||
baton->response->etag = stmt.get<std::string>(3);
|
||||
baton->response->expires = stmt.get<int64_t>(4);
|
||||
baton->response->data = stmt.get<std::string>(5);
|
||||
if (stmt.get<int>(6)) { // == compressed
|
||||
baton->response->data = util::decompress(baton->response->data);
|
||||
}
|
||||
} else {
|
||||
// There is no data.
|
||||
// This is a noop.
|
||||
}
|
||||
}, [](void *data) {
|
||||
std::unique_ptr<GetBaton> baton { (GetBaton *)data };
|
||||
if (baton->callback) {
|
||||
baton->callback(std::move(baton->response), baton->ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
struct PutBaton {
|
||||
util::ptr<Database> db;
|
||||
std::string path;
|
||||
ResourceType type;
|
||||
Response response;
|
||||
};
|
||||
|
||||
void SQLiteStore::put(const std::string &path, ResourceType type, const Response &response) {
|
||||
assert(std::this_thread::get_id() == thread_id);
|
||||
if (!db) return;
|
||||
|
||||
PutBaton *put_baton = new PutBaton;
|
||||
put_baton->db = db;
|
||||
put_baton->path = path;
|
||||
put_baton->type = type;
|
||||
put_baton->response = response;
|
||||
|
||||
uv_worker_send(worker, put_baton, [](void *data) {
|
||||
PutBaton *baton = (PutBaton *)data;
|
||||
const std::string url = unifyMapboxURLs(baton->path);
|
||||
Statement stmt = baton->db->prepare("REPLACE INTO `http_cache` ("
|
||||
// 1 2 3 4 5 6 7 8
|
||||
"`url`, `code`, `type`, `modified`, `etag`, `expires`, `data`, `compressed`"
|
||||
") VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
stmt.bind(1, url.c_str());
|
||||
stmt.bind(2, int(baton->response.code));
|
||||
stmt.bind(3, int(baton->type));
|
||||
stmt.bind(4, baton->response.modified);
|
||||
stmt.bind(5, baton->response.etag.c_str());
|
||||
stmt.bind(6, baton->response.expires);
|
||||
|
||||
if (baton->type == ResourceType::Image) {
|
||||
stmt.bind(7, baton->response.data, false); // do not retain the string internally.
|
||||
stmt.bind(8, false);
|
||||
} else {
|
||||
stmt.bind(7, util::compress(baton->response.data), true); // retain the string internally.
|
||||
stmt.bind(8, true);
|
||||
}
|
||||
|
||||
stmt.run();
|
||||
}, [](void *data) {
|
||||
delete (PutBaton *)data;
|
||||
});
|
||||
}
|
||||
|
||||
struct ExpirationBaton {
|
||||
util::ptr<Database> db;
|
||||
std::string path;
|
||||
int64_t expires;
|
||||
};
|
||||
|
||||
void SQLiteStore::updateExpiration(const std::string &path, int64_t expires) {
|
||||
assert(std::this_thread::get_id() == thread_id);
|
||||
if (!db || !*db) return;
|
||||
|
||||
ExpirationBaton *expiration_baton = new ExpirationBaton;
|
||||
expiration_baton->db = db;
|
||||
expiration_baton->path = path;
|
||||
expiration_baton->expires = expires;
|
||||
|
||||
uv_worker_send(worker, expiration_baton, [](void *data) {
|
||||
ExpirationBaton *baton = (ExpirationBaton *)data;
|
||||
const std::string url = unifyMapboxURLs(baton->path);
|
||||
Statement stmt = // 1 2
|
||||
baton->db->prepare("UPDATE `http_cache` SET `expires` = ? WHERE `url` = ?");
|
||||
stmt.bind<int64_t>(1, baton->expires);
|
||||
stmt.bind(2, url.c_str());
|
||||
stmt.run();
|
||||
}, [](void *data) {
|
||||
delete (ExpirationBaton *)data;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef MBGL_STORAGE_SQLITE_STORE
|
||||
#define MBGL_STORAGE_SQLITE_STORE
|
||||
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/storage/resource_type.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
typedef struct uv_worker_s uv_worker_t;
|
||||
|
||||
namespace mapbox {
|
||||
namespace sqlite {
|
||||
class Database;
|
||||
}
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class SQLiteStore {
|
||||
public:
|
||||
SQLiteStore(uv_loop_t *loop, const std::string &path);
|
||||
~SQLiteStore();
|
||||
|
||||
typedef void (*GetCallback)(std::unique_ptr<Response> &&entry, void *ptr);
|
||||
|
||||
void get(const std::string &path, GetCallback cb, void *ptr);
|
||||
void put(const std::string &path, ResourceType type, const Response &entry);
|
||||
void updateExpiration(const std::string &path, int64_t expires);
|
||||
|
||||
private:
|
||||
void createSchema();
|
||||
void closeDatabase();
|
||||
static void runGet(uv_work_t *req);
|
||||
static void runPut(uv_work_t *req);
|
||||
static void deliverResult(uv_work_t *req, int status);
|
||||
|
||||
private:
|
||||
const std::thread::id thread_id;
|
||||
util::ptr<mapbox::sqlite::Database> db;
|
||||
uv_worker_t *worker = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
AppliedClassProperty::AppliedClassProperty(ClassID class_id, timestamp begin_, timestamp end_, const PropertyValue &value_)
|
||||
AppliedClassProperty::AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin_, std::chrono::steady_clock::time_point end_, const PropertyValue &value_)
|
||||
: name(class_id),
|
||||
begin(begin_),
|
||||
end(end_),
|
||||
value(value_) {}
|
||||
|
||||
// Returns thie ID of the most recent
|
||||
// Returns the ID of the most recent
|
||||
ClassID AppliedClassProperties::mostRecent() const {
|
||||
return properties.size() ? properties.back().name : ClassID::Fallback;
|
||||
}
|
||||
|
||||
void AppliedClassProperties::add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value) {
|
||||
void AppliedClassProperties::add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value) {
|
||||
properties.emplace_back(class_id, begin, end, value);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ bool AppliedClassProperties::hasTransitions() const {
|
||||
|
||||
// Erase all items in the property list that are before a completed transition.
|
||||
// Then, if the only remaining property is a Fallback value, remove it too.
|
||||
void AppliedClassProperties::cleanup(timestamp now) {
|
||||
void AppliedClassProperties::cleanup(std::chrono::steady_clock::time_point now) {
|
||||
// Iterate backwards, but without using the rbegin/rend interface since we need forward
|
||||
// iterators to use .erase().
|
||||
for (auto it = properties.end(), begin = properties.begin(); it != begin;) {
|
||||
@@ -49,4 +49,4 @@ bool AppliedClassProperties::empty() const {
|
||||
return properties.empty();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
#include <mbgl/style/property_value.hpp>
|
||||
#include <mbgl/style/class_dictionary.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class AppliedClassProperty {
|
||||
public:
|
||||
AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
|
||||
AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value);
|
||||
|
||||
public:
|
||||
const ClassID name;
|
||||
const timestamp begin;
|
||||
const timestamp end;
|
||||
const std::chrono::steady_clock::time_point begin;
|
||||
const std::chrono::steady_clock::time_point end;
|
||||
const PropertyValue value;
|
||||
};
|
||||
|
||||
@@ -26,11 +26,11 @@ public:
|
||||
std::list<AppliedClassProperty> properties;
|
||||
|
||||
public:
|
||||
// Returns thie ID of the most recent
|
||||
// Returns the ID of the most recent
|
||||
ClassID mostRecent() const;
|
||||
void add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
|
||||
void add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value);
|
||||
bool hasTransitions() const;
|
||||
void cleanup(timestamp now);
|
||||
void cleanup(std::chrono::steady_clock::time_point now);
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ T FadedStopsFunction<T>::evaluate(float z, const ZoomHistory &zh) const {
|
||||
T result;
|
||||
|
||||
float fraction = std::fmod(z, 1.0f);
|
||||
float tDiff = (util::now() - zh.lastIntegerZoomTime) / 1_millisecond;
|
||||
float fDuration = duration / 1_millisecond;
|
||||
float t = std::min(tDiff / fDuration, 1.0f);
|
||||
float t = std::min((std::chrono::steady_clock::now() - zh.lastIntegerZoomTime) / duration, 1.0f);
|
||||
float scale = 1.0f;
|
||||
uint32_t low, high;
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ namespace mbgl {
|
||||
|
||||
template <typename T>
|
||||
struct FadedStopsFunction {
|
||||
inline FadedStopsFunction(const std::vector<std::pair<float, T>> &values_, timestamp duration_) : values(values_), duration(duration_) {}
|
||||
inline FadedStopsFunction(T &value) : values({{ 0, value }}), duration(300_milliseconds) {}
|
||||
inline FadedStopsFunction() : values(), duration(300_milliseconds) {}
|
||||
inline FadedStopsFunction(const std::vector<std::pair<float, T>> &values_, std::chrono::duration<float> duration_) : values(values_), duration(duration_) {}
|
||||
inline FadedStopsFunction(T &value) : values({{ 0, value }}), duration(300) {}
|
||||
inline FadedStopsFunction() : values(), duration(300) {}
|
||||
T evaluate(float z, const ZoomHistory &zoomHistory) const;
|
||||
|
||||
private:
|
||||
const std::vector<std::pair<float, T>> values;
|
||||
const timestamp duration;
|
||||
const std::chrono::duration<float> duration;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/style/property_fallback.hpp>
|
||||
#include <mbgl/style/style_properties.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -52,6 +53,48 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
|
||||
|
||||
{ PropertyKey::BackgroundOpacity, defaultStyleProperties<BackgroundProperties>().opacity },
|
||||
{ PropertyKey::BackgroundColor, defaultStyleProperties<BackgroundProperties>().color },
|
||||
|
||||
{ PropertyKey::LineCap, defaultLayoutProperties<StyleBucketLine>().cap },
|
||||
{ PropertyKey::LineJoin, defaultLayoutProperties<StyleBucketLine>().join },
|
||||
{ PropertyKey::LineMiterLimit, defaultLayoutProperties<StyleBucketLine>().miter_limit },
|
||||
{ PropertyKey::LineRoundLimit, defaultLayoutProperties<StyleBucketLine>().round_limit },
|
||||
|
||||
{ PropertyKey::SymbolPlacement, defaultLayoutProperties<StyleBucketSymbol>().placement },
|
||||
{ PropertyKey::SymbolMinDistance, defaultLayoutProperties<StyleBucketSymbol>().min_distance },
|
||||
{ PropertyKey::SymbolAvoidEdges, defaultLayoutProperties<StyleBucketSymbol>().avoid_edges },
|
||||
|
||||
{ PropertyKey::IconAllowOverlap, defaultLayoutProperties<StyleBucketSymbol>().icon.allow_overlap },
|
||||
{ PropertyKey::IconIgnorePlacement, defaultLayoutProperties<StyleBucketSymbol>().icon.ignore_placement },
|
||||
{ PropertyKey::IconOptional, defaultLayoutProperties<StyleBucketSymbol>().icon.optional },
|
||||
{ PropertyKey::IconRotationAlignment, defaultLayoutProperties<StyleBucketSymbol>().icon.rotation_alignment },
|
||||
{ PropertyKey::IconMaxSize, defaultLayoutProperties<StyleBucketSymbol>().icon.max_size },
|
||||
{ PropertyKey::IconImage, defaultLayoutProperties<StyleBucketSymbol>().icon.image },
|
||||
{ PropertyKey::IconRotate, defaultLayoutProperties<StyleBucketSymbol>().icon.rotate },
|
||||
{ PropertyKey::IconPadding, defaultLayoutProperties<StyleBucketSymbol>().icon.padding },
|
||||
{ PropertyKey::IconKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().icon.keep_upright },
|
||||
{ PropertyKey::IconOffsetX, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[0] },
|
||||
{ PropertyKey::IconOffsetY, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[1] },
|
||||
|
||||
{ PropertyKey::TextRotationAlignment, defaultLayoutProperties<StyleBucketSymbol>().text.rotation_alignment },
|
||||
{ PropertyKey::TextField, defaultLayoutProperties<StyleBucketSymbol>().text.field },
|
||||
{ PropertyKey::TextFont, defaultLayoutProperties<StyleBucketSymbol>().text.font },
|
||||
{ PropertyKey::TextMaxSize, defaultLayoutProperties<StyleBucketSymbol>().text.max_size },
|
||||
{ PropertyKey::TextMaxWidth, defaultLayoutProperties<StyleBucketSymbol>().text.max_width },
|
||||
{ PropertyKey::TextLineHeight, defaultLayoutProperties<StyleBucketSymbol>().text.line_height },
|
||||
{ PropertyKey::TextLetterSpacing, defaultLayoutProperties<StyleBucketSymbol>().text.letter_spacing },
|
||||
{ PropertyKey::TextJustify, defaultLayoutProperties<StyleBucketSymbol>().text.justify },
|
||||
{ PropertyKey::TextAnchor, defaultLayoutProperties<StyleBucketSymbol>().text.anchor },
|
||||
{ PropertyKey::TextMaxAngle, defaultLayoutProperties<StyleBucketSymbol>().text.max_angle },
|
||||
{ PropertyKey::TextRotate, defaultLayoutProperties<StyleBucketSymbol>().text.rotate },
|
||||
{ PropertyKey::TextPadding, defaultLayoutProperties<StyleBucketSymbol>().text.padding },
|
||||
{ PropertyKey::TextKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().text.keep_upright },
|
||||
{ PropertyKey::TextTransform, defaultLayoutProperties<StyleBucketSymbol>().text.transform },
|
||||
{ PropertyKey::TextOffsetX, defaultLayoutProperties<StyleBucketSymbol>().text.offset[0] },
|
||||
{ PropertyKey::TextOffsetY, defaultLayoutProperties<StyleBucketSymbol>().text.offset[1] },
|
||||
{ PropertyKey::TextAllowOverlap, defaultLayoutProperties<StyleBucketSymbol>().text.allow_overlap },
|
||||
{ PropertyKey::TextIgnorePlacement, defaultLayoutProperties<StyleBucketSymbol>().text.ignore_placement },
|
||||
{ PropertyKey::TextOptional, defaultLayoutProperties<StyleBucketSymbol>().text.optional },
|
||||
|
||||
};
|
||||
|
||||
const PropertyValue PropertyFallbackValue::defaultProperty = false;
|
||||
|
||||
@@ -26,8 +26,16 @@ enum class PropertyKey {
|
||||
LineDashArray, // for transitions only
|
||||
LineImage,
|
||||
|
||||
LineCap,
|
||||
LineJoin,
|
||||
LineMiterLimit,
|
||||
LineRoundLimit,
|
||||
|
||||
SymbolPlacement,
|
||||
SymbolMinDistance,
|
||||
SymbolAvoidEdges,
|
||||
|
||||
IconOpacity,
|
||||
IconRotate,
|
||||
IconSize,
|
||||
IconColor,
|
||||
IconHaloColor,
|
||||
@@ -38,6 +46,18 @@ enum class PropertyKey {
|
||||
IconTranslateY,
|
||||
IconTranslateAnchor,
|
||||
|
||||
IconAllowOverlap,
|
||||
IconIgnorePlacement,
|
||||
IconOptional,
|
||||
IconRotationAlignment,
|
||||
IconMaxSize,
|
||||
IconImage,
|
||||
IconPadding,
|
||||
IconRotate,
|
||||
IconKeepUpright,
|
||||
IconOffsetX,
|
||||
IconOffsetY,
|
||||
|
||||
TextOpacity,
|
||||
TextSize,
|
||||
TextColor,
|
||||
@@ -49,6 +69,26 @@ enum class PropertyKey {
|
||||
TextTranslateY,
|
||||
TextTranslateAnchor,
|
||||
|
||||
TextRotationAlignment,
|
||||
TextField,
|
||||
TextFont,
|
||||
TextMaxSize,
|
||||
TextMaxWidth,
|
||||
TextLineHeight,
|
||||
TextLetterSpacing,
|
||||
TextMaxAngle,
|
||||
TextRotate,
|
||||
TextPadding,
|
||||
TextIgnorePlacement,
|
||||
TextOptional,
|
||||
TextJustify,
|
||||
TextAnchor,
|
||||
TextKeepUpright,
|
||||
TextTransform,
|
||||
TextOffsetX,
|
||||
TextOffsetY,
|
||||
TextAllowOverlap,
|
||||
|
||||
RasterOpacity,
|
||||
RasterHueRotate,
|
||||
RasterBrightness, // for transitions only
|
||||
@@ -60,7 +100,9 @@ enum class PropertyKey {
|
||||
|
||||
BackgroundOpacity,
|
||||
BackgroundColor,
|
||||
BackgroundImage
|
||||
BackgroundImage,
|
||||
|
||||
Visibilty
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
#define MBGL_STYLE_PROPERTY_TRANSITION
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct PropertyTransition {
|
||||
uint16_t duration = 0;
|
||||
uint16_t delay = 0;
|
||||
std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero();
|
||||
std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,15 @@ typedef mapbox::util::variant<
|
||||
std::string,
|
||||
TranslateAnchorType,
|
||||
RotateAnchorType,
|
||||
bool,
|
||||
CapType,
|
||||
JoinType,
|
||||
VisibilityType,
|
||||
PlacementType,
|
||||
RotationAlignmentType,
|
||||
TextTransformType,
|
||||
TextJustifyType,
|
||||
TextAnchorType,
|
||||
Function<bool>,
|
||||
Function<float>,
|
||||
Function<Color>,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/style/style_parser.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/error.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
@@ -26,7 +25,7 @@ Style::Style()
|
||||
// for deleting the std::unique_ptr<uv::rwlock>.
|
||||
Style::~Style() {}
|
||||
|
||||
void Style::updateProperties(float z, timestamp now) {
|
||||
void Style::updateProperties(float z, std::chrono::steady_clock::time_point now) {
|
||||
uv::writelock lock(mtx);
|
||||
|
||||
zoomHistory.update(z, now);
|
||||
@@ -46,13 +45,13 @@ const std::string &Style::getSpriteURL() const {
|
||||
return sprite_url;
|
||||
}
|
||||
|
||||
void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) {
|
||||
defaultTransition.duration = duration_milliseconds;
|
||||
void Style::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) {
|
||||
defaultTransition.duration = duration;
|
||||
}
|
||||
|
||||
void Style::cascadeClasses(const std::vector<std::string>& classes) {
|
||||
if (layers) {
|
||||
layers->setClasses(classes, util::now(), defaultTransition);
|
||||
layers->setClasses(classes, std::chrono::steady_clock::now(), defaultTransition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/style/zoom_history.hpp>
|
||||
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
@@ -15,6 +14,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -22,7 +22,7 @@ class Sprite;
|
||||
class StyleLayer;
|
||||
class StyleLayerGroup;
|
||||
|
||||
class Style {
|
||||
class Style : public util::noncopyable {
|
||||
public:
|
||||
struct exception : std::runtime_error { exception(const char *msg) : std::runtime_error(msg) {} };
|
||||
|
||||
@@ -32,9 +32,9 @@ public:
|
||||
void loadJSON(const uint8_t *const data);
|
||||
|
||||
size_t layerCount() const;
|
||||
void updateProperties(float z, timestamp t);
|
||||
void updateProperties(float z, std::chrono::steady_clock::time_point now);
|
||||
|
||||
void setDefaultTransitionDuration(uint16_t duration_milliseconds = 0);
|
||||
void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero());
|
||||
void cascadeClasses(const std::vector<std::string>&);
|
||||
|
||||
bool hasTransitions() const;
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
util::ptr<StyleLayerGroup> layers;
|
||||
std::vector<std::string> appliedClasses;
|
||||
std::string glyph_url;
|
||||
std::string base;
|
||||
|
||||
private:
|
||||
std::string sprite_url;
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
StyleBucket::StyleBucket(StyleLayerType type) {
|
||||
template<> const StyleBucketFill &defaultLayoutProperties() { static StyleBucketFill p; return p; }
|
||||
template<> const StyleBucketLine &defaultLayoutProperties() { static StyleBucketLine p; return p; }
|
||||
template<> const StyleBucketSymbol &defaultLayoutProperties() { static StyleBucketSymbol p; return p; }
|
||||
template<> const StyleBucketRaster &defaultLayoutProperties() { static StyleBucketRaster p; return p; }
|
||||
template<> const StyleBucketBackground &defaultLayoutProperties() { static StyleBucketBackground p; return p; }
|
||||
|
||||
StyleBucket::StyleBucket(StyleLayerType type_) : type(type_) {
|
||||
switch (type) {
|
||||
case StyleLayerType::Fill: render = StyleBucketFill{}; break;
|
||||
case StyleLayerType::Line: render = StyleBucketLine{}; break;
|
||||
case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break;
|
||||
case StyleLayerType::Raster: render = StyleBucketRaster{}; break;
|
||||
case StyleLayerType::Fill: render.set<StyleBucketFill>(); break;
|
||||
case StyleLayerType::Line: render.set<StyleBucketLine>(); break;
|
||||
case StyleLayerType::Symbol: render.set<StyleBucketSymbol>(); break;
|
||||
case StyleLayerType::Raster: render.set<StyleBucketRaster>(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/style/class_properties.hpp>
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
@@ -18,11 +19,23 @@ class Source;
|
||||
|
||||
class StyleBucketFill {
|
||||
public:
|
||||
WindingType winding = WindingType::NonZero;
|
||||
// Make movable only.
|
||||
StyleBucketFill() = default;
|
||||
StyleBucketFill(StyleBucketFill &&) = default;
|
||||
StyleBucketFill& operator=(StyleBucketFill &&) = default;
|
||||
StyleBucketFill(const StyleBucketFill &) = delete;
|
||||
StyleBucketFill& operator=(const StyleBucketFill &) = delete;
|
||||
};
|
||||
|
||||
class StyleBucketLine {
|
||||
public:
|
||||
// Make movable only.
|
||||
StyleBucketLine() = default;
|
||||
StyleBucketLine(StyleBucketLine &&) = default;
|
||||
StyleBucketLine& operator=(StyleBucketLine &&) = default;
|
||||
StyleBucketLine(const StyleBucketLine &) = delete;
|
||||
StyleBucketLine& operator=(const StyleBucketLine &) = delete;
|
||||
|
||||
CapType cap = CapType::Butt;
|
||||
JoinType join = JoinType::Miter;
|
||||
float miter_limit = 2.0f;
|
||||
@@ -32,11 +45,11 @@ public:
|
||||
class StyleBucketSymbol {
|
||||
public:
|
||||
// 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;
|
||||
StyleBucketSymbol() = default;
|
||||
StyleBucketSymbol(StyleBucketSymbol &&) = default;
|
||||
StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default;
|
||||
StyleBucketSymbol(const StyleBucketSymbol &) = delete;
|
||||
StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete;
|
||||
|
||||
PlacementType placement = PlacementType::Point;
|
||||
float min_distance = 250.0f;
|
||||
@@ -52,7 +65,7 @@ public:
|
||||
float rotate = 0.0f;
|
||||
float padding = 2.0f;
|
||||
bool keep_upright = false;
|
||||
vec2<float> offset = {0, 0};
|
||||
std::array<float, 2> offset = {{ 0, 0 }};
|
||||
} icon;
|
||||
|
||||
struct {
|
||||
@@ -60,18 +73,17 @@ public:
|
||||
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 */;
|
||||
float max_width = 15.0f /* em */;
|
||||
float line_height = 1.2f /* em */;
|
||||
float letter_spacing = 0.0f /* em */;
|
||||
TextJustifyType justify = TextJustifyType::Center;
|
||||
TextAnchorType anchor = TextAnchorType::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};
|
||||
std::array<float, 2> offset = {{ 0, 0 }};
|
||||
bool allow_overlap = false;
|
||||
bool ignore_placement = false;
|
||||
bool optional = false;
|
||||
@@ -79,30 +91,42 @@ public:
|
||||
};
|
||||
|
||||
class StyleBucketRaster {
|
||||
public:
|
||||
// Make movable only.
|
||||
StyleBucketRaster() = default;
|
||||
StyleBucketRaster(StyleBucketRaster &&) = default;
|
||||
StyleBucketRaster& operator=(StyleBucketRaster &&) = default;
|
||||
StyleBucketRaster(const StyleBucketRaster &) = delete;
|
||||
StyleBucketRaster& operator=(const StyleBucketRaster &) = delete;
|
||||
};
|
||||
|
||||
class StyleBucketBackground {
|
||||
public:
|
||||
};
|
||||
|
||||
typedef mapbox::util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbol,
|
||||
StyleBucketRaster, std::false_type> StyleBucketRender;
|
||||
StyleBucketRaster, StyleBucketBackground, std::false_type> StyleBucketRender;
|
||||
|
||||
|
||||
class StyleBucket {
|
||||
class StyleBucket : public util::noncopyable {
|
||||
public:
|
||||
typedef util::ptr<StyleBucket> Ptr;
|
||||
|
||||
StyleBucket(StyleLayerType type);
|
||||
|
||||
StyleLayerType type;
|
||||
std::string name;
|
||||
util::ptr<StyleSource> style_source;
|
||||
std::string source_layer;
|
||||
FilterExpression filter;
|
||||
ClassProperties layout;
|
||||
StyleBucketRender render = std::false_type();
|
||||
float min_zoom = -std::numeric_limits<float>::infinity();
|
||||
float max_zoom = std::numeric_limits<float>::infinity();
|
||||
VisibilityType visibility = VisibilityType::Visible;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
const T &defaultLayoutProperties();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ bool StyleLayer::isBackground() const {
|
||||
return type == StyleLayerType::Background;
|
||||
}
|
||||
|
||||
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now,
|
||||
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const std::chrono::steady_clock::time_point now,
|
||||
const PropertyTransition &defaultTransition) {
|
||||
// Stores all keys that we have already added transitions for.
|
||||
std::set<PropertyKey> already_applied;
|
||||
@@ -45,8 +45,8 @@ void StyleLayer::setClasses(const std::vector<std::string> &class_names, const t
|
||||
if (appliedProperties.mostRecent() != ClassID::Fallback) {
|
||||
// This property key hasn't been set by a previous class, so we need to add a transition
|
||||
// to the fallback value for that key.
|
||||
const timestamp begin = now + defaultTransition.delay * 1_millisecond;
|
||||
const timestamp end = begin + defaultTransition.duration * 1_millisecond;
|
||||
const std::chrono::steady_clock::time_point begin = now + defaultTransition.delay;
|
||||
const std::chrono::steady_clock::time_point end = begin + defaultTransition.duration;
|
||||
const PropertyValue &value = PropertyFallbackValue::Get(key);
|
||||
appliedProperties.add(ClassID::Fallback, begin, end, value);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ void StyleLayer::setClasses(const std::vector<std::string> &class_names, const t
|
||||
|
||||
// Helper function for applying all properties of a a single class that haven't been applied yet.
|
||||
void StyleLayer::applyClassProperties(const ClassID class_id,
|
||||
std::set<PropertyKey> &already_applied, timestamp now,
|
||||
std::set<PropertyKey> &already_applied, std::chrono::steady_clock::time_point now,
|
||||
const PropertyTransition &defaultTransition) {
|
||||
auto style_it = styles.find(class_id);
|
||||
if (style_it == styles.end()) {
|
||||
@@ -83,8 +83,8 @@ void StyleLayer::applyClassProperties(const ClassID class_id,
|
||||
if (appliedProperties.mostRecent() != class_id) {
|
||||
const PropertyTransition &transition =
|
||||
class_properties.getTransition(key, defaultTransition);
|
||||
const timestamp begin = now + transition.delay * 1_millisecond;
|
||||
const timestamp end = begin + transition.duration * 1_millisecond;
|
||||
const std::chrono::steady_clock::time_point begin = now + transition.delay;
|
||||
const std::chrono::steady_clock::time_point end = begin + transition.duration;
|
||||
const PropertyValue &value = property_pair.second;
|
||||
appliedProperties.add(class_id, begin, end, value);
|
||||
}
|
||||
@@ -120,7 +120,7 @@ private:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
auto it = appliedStyle.find(key);
|
||||
if (it != appliedStyle.end()) {
|
||||
AppliedClassProperties &applied = it->second;
|
||||
@@ -138,7 +138,7 @@ void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, c
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
auto it = appliedStyle.find(key);
|
||||
if (it != appliedStyle.end()) {
|
||||
AppliedClassProperties &applied = it->second;
|
||||
@@ -150,7 +150,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons
|
||||
target = mapbox::util::apply_visitor(evaluator, property.value);
|
||||
} else if (now >= property.begin) {
|
||||
// We overwrite the current property partially with the new value.
|
||||
float progress = float(now - property.begin) / float(property.end - property.begin);
|
||||
float progress = std::chrono::duration<float>(now - property.begin) / (property.end - property.begin);
|
||||
target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress);
|
||||
} else {
|
||||
// Do not apply this property because its transition hasn't begun yet.
|
||||
@@ -160,7 +160,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperties<FillProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
properties.set<FillProperties>();
|
||||
FillProperties &fill = properties.get<FillProperties>();
|
||||
applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now, zoomHistory);
|
||||
@@ -174,7 +174,7 @@ void StyleLayer::applyStyleProperties<FillProperties>(const float z, const times
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperties<LineProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
properties.set<LineProperties>();
|
||||
LineProperties &line = properties.get<LineProperties>();
|
||||
applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now, zoomHistory);
|
||||
@@ -189,11 +189,11 @@ void StyleLayer::applyStyleProperties<LineProperties>(const float z, const times
|
||||
applyStyleProperty(PropertyKey::LineImage, line.image, z, now, zoomHistory);
|
||||
|
||||
// for scaling dasharrays
|
||||
applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), now + 10000, zoomHistory);
|
||||
applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), std::chrono::steady_clock::time_point::max(), zoomHistory);
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
properties.set<SymbolProperties>();
|
||||
SymbolProperties &symbol = properties.get<SymbolProperties>();
|
||||
applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory);
|
||||
@@ -219,7 +219,7 @@ void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const tim
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
properties.set<RasterProperties>();
|
||||
RasterProperties &raster = properties.get<RasterProperties>();
|
||||
applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now, zoomHistory);
|
||||
@@ -232,7 +232,7 @@ void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const tim
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
|
||||
properties.set<BackgroundProperties>();
|
||||
BackgroundProperties &background = properties.get<BackgroundProperties>();
|
||||
applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now, zoomHistory);
|
||||
@@ -240,7 +240,7 @@ void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const
|
||||
applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now, zoomHistory);
|
||||
}
|
||||
|
||||
void StyleLayer::updateProperties(float z, const timestamp now, ZoomHistory &zoomHistory) {
|
||||
void StyleLayer::updateProperties(float z, const std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) {
|
||||
cleanupAppliedStyleProperties(now);
|
||||
|
||||
switch (type) {
|
||||
@@ -263,7 +263,7 @@ bool StyleLayer::hasTransitions() const {
|
||||
}
|
||||
|
||||
|
||||
void StyleLayer::cleanupAppliedStyleProperties(timestamp now) {
|
||||
void StyleLayer::cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now) {
|
||||
auto it = appliedStyle.begin();
|
||||
const auto end = appliedStyle.end();
|
||||
while (it != end) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -36,10 +37,10 @@ public:
|
||||
|
||||
// Updates the StyleProperties information in this layer by evaluating all
|
||||
// pending transitions and applied classes in order.
|
||||
void updateProperties(float z, timestamp now, ZoomHistory &zoomHistory);
|
||||
void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory);
|
||||
|
||||
// Sets the list of classes and creates transitions to the currently applied values.
|
||||
void setClasses(const std::vector<std::string> &class_names, timestamp now,
|
||||
void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now,
|
||||
const PropertyTransition &defaultTransition);
|
||||
|
||||
bool hasTransitions() const;
|
||||
@@ -47,16 +48,16 @@ public:
|
||||
private:
|
||||
// Applies all properties from a class, if they haven't been applied already.
|
||||
void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
|
||||
timestamp now, const PropertyTransition &defaultTransition);
|
||||
std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition);
|
||||
|
||||
// Sets the properties of this object by evaluating all pending transitions and
|
||||
// aplied classes in order.
|
||||
template <typename T> void applyStyleProperties(float z, timestamp now, const ZoomHistory &zoomHistory);
|
||||
template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp now, const ZoomHistory &zoomHistory);
|
||||
template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, timestamp now, const ZoomHistory &zoomHistory);
|
||||
template <typename T> void applyStyleProperties(float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
|
||||
template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
|
||||
template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
|
||||
|
||||
// Removes all expired style transitions.
|
||||
void cleanupAppliedStyleProperties(timestamp now);
|
||||
void cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now);
|
||||
|
||||
public:
|
||||
// The name of this layer.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, timestamp now,
|
||||
void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now,
|
||||
const PropertyTransition &defaultTransition) {
|
||||
for (const util::ptr<StyleLayer> &layer : layers) {
|
||||
if (layer) {
|
||||
@@ -11,10 +11,10 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, ti
|
||||
}
|
||||
}
|
||||
|
||||
void StyleLayerGroup::updateProperties(float z, timestamp t, ZoomHistory &zoomHistory) {
|
||||
void StyleLayerGroup::updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) {
|
||||
for (const util::ptr<StyleLayer> &layer: layers) {
|
||||
if (layer) {
|
||||
layer->updateProperties(z, t, zoomHistory);
|
||||
layer->updateProperties(z, now, zoomHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class StyleLayerGroup {
|
||||
public:
|
||||
void setClasses(const std::vector<std::string> &class_names, timestamp now,
|
||||
void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now,
|
||||
const PropertyTransition &defaultTransition);
|
||||
void updateProperties(float z, timestamp t, ZoomHistory &zoomHistory);
|
||||
void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory);
|
||||
|
||||
bool hasTransitions() const;
|
||||
public:
|
||||
|
||||
@@ -341,7 +341,7 @@ std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
|
||||
return std::tuple<bool, Function<T>> { true, StopsFunction<T>(std::get<1>(stops), base) };
|
||||
}
|
||||
|
||||
template <typename T> inline timestamp defaultDurationValue() { return 300_milliseconds; }
|
||||
template <typename T> inline std::chrono::duration<float> defaultDurationValue() { return std::chrono::duration<float>(300.0f); }
|
||||
|
||||
template <typename T>
|
||||
std::tuple<bool, FadedStopsFunction<T>> StyleParser::parseFadedStopsFunction(JSVal value) {
|
||||
@@ -350,13 +350,12 @@ std::tuple<bool, FadedStopsFunction<T>> StyleParser::parseFadedStopsFunction(JSV
|
||||
return std::tuple<bool, FadedStopsFunction<T>> { false, {} };
|
||||
}
|
||||
|
||||
timestamp duration = defaultDurationValue<T>();
|
||||
std::chrono::duration<float> duration = defaultDurationValue<T>();
|
||||
|
||||
if (value.HasMember("duration")) {
|
||||
JSVal value_duration = value["duration"];
|
||||
if (value_duration.IsNumber()) {
|
||||
float duration_ = value_duration.GetDouble();
|
||||
duration = 1_millisecond * duration_;
|
||||
duration = static_cast<std::chrono::duration<float>>(value_duration.GetDouble());
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "duration must be numeric");
|
||||
}
|
||||
@@ -380,6 +379,18 @@ bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKe
|
||||
return std::get<0>(res);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void StyleParser::parseVisibility(StyleBucket &bucket, JSVal value) {
|
||||
if (!value.HasMember("visibility")) {
|
||||
return;
|
||||
} else if (!value["visibility"].IsString()) {
|
||||
Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string");
|
||||
bucket.visibility = VisibilityType::Visible;
|
||||
return;
|
||||
}
|
||||
bucket.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
@@ -398,6 +409,15 @@ template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value,
|
||||
return std::tuple<bool, std::string> { true, { value.GetString(), value.GetStringLength() } };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, bool> StyleParser::parseProperty(JSVal value, const char *property_name) {
|
||||
if (!value.IsBool()) {
|
||||
Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", property_name);
|
||||
return std::tuple<bool, bool> { false, true };
|
||||
}
|
||||
|
||||
return std::tuple<bool, bool> { true, value.GetBool() };
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -416,18 +436,81 @@ template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateA
|
||||
return std::tuple<bool, RotateAnchorType> { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, CapType> StyleParser::parseProperty<CapType>(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, CapType> { false, CapType::Butt };
|
||||
}
|
||||
|
||||
return std::tuple<bool, CapType> { true, CapTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, JoinType> StyleParser::parseProperty<JoinType>(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, JoinType> { false, JoinType::Miter };
|
||||
}
|
||||
|
||||
return std::tuple<bool, JoinType> { true, JoinTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, PlacementType> StyleParser::parseProperty<PlacementType>(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, PlacementType> { false, PlacementType::Point };
|
||||
}
|
||||
|
||||
return std::tuple<bool, PlacementType> { true, PlacementTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, TextAnchorType> StyleParser::parseProperty<TextAnchorType>(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, TextAnchorType> { false, TextAnchorType::Center };
|
||||
}
|
||||
|
||||
return std::tuple<bool, TextAnchorType> { true, TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, TextJustifyType> StyleParser::parseProperty<TextJustifyType>(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, TextJustifyType> { false, TextJustifyType::Center };
|
||||
}
|
||||
|
||||
return std::tuple<bool, TextJustifyType> { true, TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, TextTransformType> StyleParser::parseProperty<TextTransformType>(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, TextTransformType> { false, TextTransformType::None };
|
||||
}
|
||||
|
||||
return std::tuple<bool, TextTransformType> { true, TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
template<> std::tuple<bool, RotationAlignmentType> StyleParser::parseProperty<RotationAlignmentType>(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, RotationAlignmentType> { false, RotationAlignmentType::Map };
|
||||
}
|
||||
|
||||
return std::tuple<bool, RotationAlignmentType> { true, RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) };
|
||||
}
|
||||
|
||||
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()) {
|
||||
transition.duration = value["duration"].GetUint();
|
||||
transition.duration = std::chrono::milliseconds(value["duration"].GetUint());
|
||||
}
|
||||
if (value.HasMember("delay") && value["delay"].IsNumber()) {
|
||||
transition.delay = value["delay"].GetUint();
|
||||
transition.delay = std::chrono::milliseconds(value["delay"].GetUint());
|
||||
}
|
||||
}
|
||||
|
||||
if (transition.duration == 0 && transition.delay == 0) {
|
||||
if (transition.duration == std::chrono::steady_clock::duration::zero() && transition.delay == std::chrono::steady_clock::duration::zero()) {
|
||||
return std::tuple<bool, PropertyTransition> { false, std::move(transition) };
|
||||
}
|
||||
|
||||
@@ -710,6 +793,50 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
|
||||
parseOptionalProperty<FadedStopsFunction<Faded<std::string>>>("background-image", Key::BackgroundImage, klass, value);
|
||||
}
|
||||
|
||||
void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
|
||||
using Key = PropertyKey;
|
||||
|
||||
parseVisibility<VisibilityType>(*bucket, value);
|
||||
|
||||
parseOptionalProperty<CapType>("line-cap", Key::LineCap, bucket->layout, value);
|
||||
parseOptionalProperty<JoinType>("line-join", Key::LineJoin, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("line-miter-limit", Key::LineMiterLimit, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("line-round-limit", Key::LineRoundLimit, bucket->layout, value);
|
||||
|
||||
parseOptionalProperty<PlacementType>("symbol-placement", Key::SymbolPlacement, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("icon-optional", Key::IconOptional, bucket->layout, value);
|
||||
parseOptionalProperty<RotationAlignmentType>("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("icon-max-size", Key::IconMaxSize, bucket->layout, value);
|
||||
parseOptionalProperty<std::string>("icon-image", Key::IconImage, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("icon-padding", Key::IconPadding, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("icon-offset", { Key::IconOffsetX, Key::IconOffsetY }, bucket->layout, value);
|
||||
parseOptionalProperty<RotationAlignmentType>("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value);
|
||||
parseOptionalProperty<std::string>("text-field", Key::TextField, bucket->layout, value);
|
||||
parseOptionalProperty<std::string>("text-font", Key::TextFont, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-max-size", Key::TextMaxSize, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-max-width", Key::TextMaxWidth, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-line-height", Key::TextLineHeight, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value);
|
||||
parseOptionalProperty<TextJustifyType>("text-justify", Key::TextJustify, bucket->layout, value);
|
||||
parseOptionalProperty<TextAnchorType>("text-anchor", Key::TextAnchor, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-max-angle", Key::TextMaxAngle, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-rotate", Key::TextRotate, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-padding", Key::TextPadding, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("text-keep-upright", Key::TextKeepUpright, bucket->layout, value);
|
||||
parseOptionalProperty<TextTransformType>("text-transform", Key::TextTransform, bucket->layout, value);
|
||||
parseOptionalProperty<Function<float>>("text-offset", { Key::TextOffsetX, Key::TextOffsetY }, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value);
|
||||
parseOptionalProperty<bool>("text-optional", Key::TextOptional, bucket->layout, value);
|
||||
|
||||
}
|
||||
|
||||
void StyleParser::parseReference(JSVal value, util::ptr<StyleLayer> &layer) {
|
||||
if (!value.IsString()) {
|
||||
Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str());
|
||||
@@ -772,7 +899,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
|
||||
|
||||
if (value.HasMember("layout")) {
|
||||
JSVal value_render = replaceConstant(value["layout"]);
|
||||
parseLayout(value_render, layer);
|
||||
parseLayout(value_render, layer->bucket);
|
||||
}
|
||||
|
||||
if (value.HasMember("minzoom")) {
|
||||
@@ -794,89 +921,6 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
|
||||
}
|
||||
}
|
||||
|
||||
void StyleParser::parseLayout(JSVal value, util::ptr<StyleLayer> &layer) {
|
||||
if (!value.IsObject()) {
|
||||
Log::Warning(Event::ParseStyle, "layout property of layer '%s' must be an object", layer->id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
StyleBucket &bucket = *layer->bucket;
|
||||
parseRenderProperty<VisibilityTypeClass>(value, bucket.visibility, "visibility");
|
||||
|
||||
switch (layer->type) {
|
||||
case StyleLayerType::Fill: {
|
||||
StyleBucketFill &render = bucket.render.get<StyleBucketFill>();
|
||||
|
||||
parseRenderProperty<WindingTypeClass>(value, render.winding, "fill-winding");
|
||||
} break;
|
||||
|
||||
case StyleLayerType::Line: {
|
||||
StyleBucketLine &render = bucket.render.get<StyleBucketLine>();
|
||||
|
||||
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::Symbol: {
|
||||
StyleBucketSymbol &render = bucket.render.get<StyleBucketSymbol>();
|
||||
|
||||
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.min_distance, "symbol-min-distance");
|
||||
parseRenderProperty(value, render.avoid_edges, "symbol-avoid-edges");
|
||||
|
||||
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<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.text.line_height, "text-line-height")) {
|
||||
render.text.line_height *= 24; // em
|
||||
}
|
||||
if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) {
|
||||
render.text.letter_spacing *= 24; // em
|
||||
}
|
||||
parseRenderProperty<TextJustifyTypeClass>(value, render.text.justify, "text-justify");
|
||||
parseRenderProperty<TextAnchorTypeClass>(value, render.text.anchor, "text-anchor");
|
||||
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(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.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StyleParser::parseSprite(JSVal value) {
|
||||
if (value.IsString()) {
|
||||
sprite = { value.GetString(), value.GetStringLength() };
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
void parsePaint(JSVal, ClassProperties &properties);
|
||||
void parseReference(JSVal value, util::ptr<StyleLayer> &layer);
|
||||
void parseBucket(JSVal value, util::ptr<StyleLayer> &layer);
|
||||
void parseLayout(JSVal value, util::ptr<StyleLayer> &layer);
|
||||
void parseLayout(JSVal value, util::ptr<StyleBucket> &bucket);
|
||||
void parseSprite(JSVal value);
|
||||
void parseGlyphURL(JSVal value);
|
||||
|
||||
@@ -65,6 +65,8 @@ private:
|
||||
|
||||
// Parses optional properties into style class properties.
|
||||
template <typename T>
|
||||
void parseVisibility(StyleBucket &bucket, JSVal value);
|
||||
template <typename T>
|
||||
bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value);
|
||||
template <typename T>
|
||||
bool parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
|
||||
|
||||
@@ -72,18 +72,6 @@ MBGL_DEFINE_ENUM_CLASS(VisibilityTypeClass, VisibilityType, {
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class WindingType : bool {
|
||||
EvenOdd,
|
||||
NonZero,
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, {
|
||||
{ WindingType::EvenOdd, "even-odd" },
|
||||
{ WindingType::NonZero, "non-zero" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class CapType : uint8_t {
|
||||
Round,
|
||||
Butt,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifndef MBGL_STYLE_ZOOM_HISTORY
|
||||
#define MBGL_STYLE_ZOOM_HISTORY
|
||||
|
||||
#include <mbgl/util/time.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -10,15 +9,15 @@ namespace mbgl {
|
||||
struct ZoomHistory {
|
||||
float lastZoom;
|
||||
float lastIntegerZoom;
|
||||
timestamp lastIntegerZoomTime;
|
||||
std::chrono::steady_clock::time_point lastIntegerZoomTime;
|
||||
bool first = true;
|
||||
|
||||
void update(float z, timestamp now) {
|
||||
void update(float z, std::chrono::steady_clock::time_point now) {
|
||||
if (first) {
|
||||
first = false;
|
||||
|
||||
lastIntegerZoom = std::floor(z);
|
||||
lastIntegerZoomTime = 0;
|
||||
lastIntegerZoomTime = std::chrono::steady_clock::time_point::min();
|
||||
lastZoom = z;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,28 +148,23 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
|
||||
});
|
||||
|
||||
// The prepare call jumps back to the main thread.
|
||||
fileSource.prepare([&, url] {
|
||||
auto request = fileSource.request(ResourceType::Glyphs, url);
|
||||
request->onload([&, url](const 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 = std::string { "[ERROR] failed to load glyphs (" } + util::toString(res.code) + "): " + res.message;
|
||||
promise.set_exception(std::make_exception_ptr(std::runtime_error(msg)));
|
||||
} else {
|
||||
// Transfer the data to the GlyphSet and signal its availability.
|
||||
// Once it is available, the caller will need to call parse() to actually
|
||||
// parse the data we received. We are not doing this here since this callback is being
|
||||
// called from another (unknown) thread.
|
||||
data = res.data;
|
||||
promise.set_value(*this);
|
||||
}
|
||||
});
|
||||
request->oncancel([&]() {
|
||||
promise.set_exception(std::make_exception_ptr(std::runtime_error("Loading glyphs was canceled")));
|
||||
});
|
||||
fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
|
||||
if (res.status != Response::Successful) {
|
||||
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
|
||||
const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message;
|
||||
promise.set_exception(std::make_exception_ptr(std::runtime_error(msg)));
|
||||
} else {
|
||||
// Transfer the data to the GlyphSet and signal its availability.
|
||||
// Once it is available, the caller will need to call parse() to actually
|
||||
// parse the data we received. We are not doing this here since this callback is being
|
||||
// called from another (unknown) thread.
|
||||
data = res.data;
|
||||
promise.set_value(*this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::shared_future<GlyphPBF &> GlyphPBF::getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) {
|
||||
Placement Placement::getIcon(Anchor &anchor, const Rect<uint16_t> &image, float boxScale,
|
||||
const std::vector<Coordinate> &line, const StyleBucketSymbol &props) {
|
||||
|
||||
const float dx = props.icon.offset.x;
|
||||
const float dy = props.icon.offset.y;
|
||||
const float dx = props.icon.offset[0];
|
||||
const float dy = props.icon.offset[1];
|
||||
float x1 = dx - image.originalW / 2.0f;
|
||||
float x2 = x1 + image.w;
|
||||
float y1 = dy - image.originalH / 2.0f;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#include <mbgl/util/compression.hpp>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
std::string compress(const std::string &raw) {
|
||||
z_stream deflate_stream;
|
||||
memset(&deflate_stream, 0, sizeof(deflate_stream));
|
||||
|
||||
// TODO: reuse z_streams
|
||||
if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
|
||||
throw std::runtime_error("failed to initialize deflate");
|
||||
}
|
||||
|
||||
deflate_stream.next_in = (Bytef *)raw.data();
|
||||
deflate_stream.avail_in = uInt(raw.size());
|
||||
|
||||
std::string result;
|
||||
char out[16384];
|
||||
|
||||
int code;
|
||||
do {
|
||||
deflate_stream.next_out = reinterpret_cast<Bytef *>(out);
|
||||
deflate_stream.avail_out = sizeof(out);
|
||||
code = deflate(&deflate_stream, Z_FINISH);
|
||||
if (result.size() < deflate_stream.total_out) {
|
||||
// append the block to the output string
|
||||
result.append(out, deflate_stream.total_out - result.size());
|
||||
}
|
||||
} while (code == Z_OK);
|
||||
|
||||
deflateEnd(&deflate_stream);
|
||||
|
||||
if (code != Z_STREAM_END) {
|
||||
throw std::runtime_error(deflate_stream.msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string decompress(const std::string &raw) {
|
||||
z_stream inflate_stream;
|
||||
memset(&inflate_stream, 0, sizeof(inflate_stream));
|
||||
|
||||
// TODO: reuse z_streams
|
||||
if (inflateInit(&inflate_stream) != Z_OK) {
|
||||
throw std::runtime_error("failed to initialize inflate");
|
||||
}
|
||||
|
||||
inflate_stream.next_in = (Bytef *)raw.data();
|
||||
inflate_stream.avail_in = uInt(raw.size());
|
||||
|
||||
std::string result;
|
||||
char out[15384];
|
||||
|
||||
int code;
|
||||
do {
|
||||
inflate_stream.next_out = reinterpret_cast<Bytef *>(out);
|
||||
inflate_stream.avail_out = sizeof(out);
|
||||
code = inflate(&inflate_stream, 0);
|
||||
// result.append(out, sizeof(out) - inflate_stream.avail_out);
|
||||
if (result.size() < inflate_stream.total_out) {
|
||||
result.append(out, inflate_stream.total_out - result.size());
|
||||
}
|
||||
} while (code == Z_OK);
|
||||
|
||||
inflateEnd(&inflate_stream);
|
||||
|
||||
if (code != Z_STREAM_END) {
|
||||
throw std::runtime_error(inflate_stream.msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef MBGL_UTIL_COMPRESSION
|
||||
#define MBGL_UTIL_COMPRESSION
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
std::string compress(const std::string &raw);
|
||||
std::string decompress(const std::string &raw);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
99
src/mbgl/util/merge_lines.cpp
Normal file
99
src/mbgl/util/merge_lines.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "merge_lines.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
unsigned int mergeFromRight(std::vector<SymbolFeature> &features,
|
||||
std::map<std::string, unsigned int> &rightIndex,
|
||||
std::map<std::string, unsigned int>::iterator left,
|
||||
std::string &rightKey,
|
||||
std::vector<std::vector<Coordinate>> &geom) {
|
||||
|
||||
unsigned int index = left->second;
|
||||
rightIndex.erase(left);
|
||||
rightIndex[rightKey] = index;
|
||||
features[index].geometry[0].pop_back();
|
||||
features[index].geometry[0].insert(
|
||||
features[index].geometry[0].end(), geom[0].begin(), geom[0].end());
|
||||
geom[0].clear();
|
||||
return index;
|
||||
}
|
||||
|
||||
unsigned int mergeFromLeft(std::vector<SymbolFeature> &features,
|
||||
std::map<std::string, unsigned int> &leftIndex,
|
||||
std::string &leftKey,
|
||||
std::map<std::string, unsigned int>::iterator right,
|
||||
std::vector<std::vector<Coordinate>> &geom) {
|
||||
|
||||
unsigned int index = right->second;
|
||||
leftIndex.erase(right);
|
||||
leftIndex[leftKey] = index;
|
||||
geom[0].pop_back();
|
||||
geom[0].insert(
|
||||
geom[0].end(), features[index].geometry[0].begin(), features[index].geometry[0].end());
|
||||
features[index].geometry[0].clear();
|
||||
std::swap(features[index].geometry[0], geom[0]);
|
||||
return index;
|
||||
}
|
||||
|
||||
std::string
|
||||
getKey(const std::u32string &text, const std::vector<std::vector<Coordinate>> &geom, bool onRight) {
|
||||
const Coordinate &coord = onRight ? geom[0].back() : geom[0].front();
|
||||
std::ostringstream key;
|
||||
for (const char32_t &c : text) {
|
||||
key << (char)c;
|
||||
}
|
||||
key << ":" << coord.x << ":" << coord.y;
|
||||
return key.str();
|
||||
}
|
||||
|
||||
void mergeLines(std::vector<SymbolFeature> &features) {
|
||||
|
||||
std::map<std::string, unsigned int> leftIndex;
|
||||
std::map<std::string, unsigned int> rightIndex;
|
||||
|
||||
for (unsigned int k = 0; k < features.size(); k++) {
|
||||
SymbolFeature &feature = features[k];
|
||||
std::vector<std::vector<Coordinate>> &geometry = feature.geometry;
|
||||
|
||||
if (!feature.label.length()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string leftKey = getKey(feature.label, geometry, false);
|
||||
std::string rightKey = getKey(feature.label, geometry, true);
|
||||
|
||||
auto left = rightIndex.find(leftKey);
|
||||
auto right = leftIndex.find(rightKey);
|
||||
|
||||
if ((left != rightIndex.end()) && (right != leftIndex.end()) &&
|
||||
(left->second != right->second)) {
|
||||
// found lines with the same text adjacent to both ends of the current line, merge all
|
||||
// three
|
||||
unsigned int j = mergeFromLeft(features, leftIndex, leftKey, right, geometry);
|
||||
unsigned int i =
|
||||
mergeFromRight(features, rightIndex, left, rightKey, features[j].geometry);
|
||||
|
||||
leftIndex.erase(leftKey);
|
||||
rightIndex.erase(rightKey);
|
||||
rightIndex[getKey(feature.label, features[i].geometry, true)] = i;
|
||||
|
||||
} else if (left != rightIndex.end()) {
|
||||
// found mergeable line adjacent to the start of the current line, merge
|
||||
mergeFromRight(features, rightIndex, left, rightKey, geometry);
|
||||
|
||||
} else if (right != leftIndex.end()) {
|
||||
// found mergeable line adjacent to the end of the current line, merge
|
||||
mergeFromLeft(features, leftIndex, leftKey, right, geometry);
|
||||
|
||||
} else {
|
||||
// no adjacent lines, add as a new item
|
||||
leftIndex[leftKey] = k;
|
||||
rightIndex[rightKey] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace util
|
||||
} // end namespace mbgl
|
||||
@@ -4,102 +4,24 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <mbgl/renderer/symbol_bucket.hpp>
|
||||
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
unsigned int mergeFromRight(
|
||||
std::vector<SymbolFeature> &features,
|
||||
std::map<std::string,unsigned int> &rightIndex,
|
||||
std::map<std::string,unsigned int>::iterator left,
|
||||
std::string &rightKey,
|
||||
std::vector<std::vector<Coordinate>> &geom) {
|
||||
unsigned int mergeFromRight(std::vector<SymbolFeature> &features,
|
||||
std::map<std::string, unsigned int> &rightIndex,
|
||||
std::map<std::string, unsigned int>::iterator left,
|
||||
std::string &rightKey,
|
||||
std::vector<std::vector<Coordinate>> &geom);
|
||||
|
||||
unsigned int index = left->second;
|
||||
rightIndex.erase(left);
|
||||
rightIndex[rightKey] = index;
|
||||
features[index].geometry[0].pop_back();
|
||||
features[index].geometry[0].insert(features[index].geometry[0].end(), geom[0].begin(), geom[0].end());
|
||||
geom[0].clear();
|
||||
return index;
|
||||
}
|
||||
unsigned int mergeFromLeft(std::vector<SymbolFeature> &features,
|
||||
std::map<std::string, unsigned int> &leftIndex,
|
||||
std::string &leftKey,
|
||||
std::map<std::string, unsigned int>::iterator right,
|
||||
std::vector<std::vector<Coordinate>> &geom);
|
||||
|
||||
unsigned int mergeFromLeft(
|
||||
std::vector<SymbolFeature> &features,
|
||||
std::map<std::string,unsigned int> &leftIndex,
|
||||
std::string &leftKey,
|
||||
std::map<std::string,unsigned int>::iterator right,
|
||||
std::vector<std::vector<Coordinate>> &geom) {
|
||||
|
||||
unsigned int index = right->second;
|
||||
leftIndex.erase(right);
|
||||
leftIndex[leftKey] = index;
|
||||
geom[0].pop_back();
|
||||
geom[0].insert(geom[0].end(), features[index].geometry[0].begin(), features[index].geometry[0].end());
|
||||
features[index].geometry[0].clear();
|
||||
std::swap(features[index].geometry[0], geom[0]);
|
||||
return index;
|
||||
}
|
||||
|
||||
std::string getKey(const std::u32string &text, const std::vector<std::vector<Coordinate>> &geom, bool onRight) {
|
||||
const Coordinate &coord = onRight ? geom[0].back() : geom[0].front();
|
||||
std::ostringstream key;
|
||||
for (const char32_t &c : text) {
|
||||
key << (char)c;
|
||||
}
|
||||
key << ":" << coord.x << ":" << coord.y;
|
||||
return key.str();
|
||||
}
|
||||
|
||||
|
||||
void mergeLines(std::vector<SymbolFeature> &features) {
|
||||
|
||||
std::map<std::string,unsigned int> leftIndex;
|
||||
std::map<std::string,unsigned int> rightIndex;
|
||||
|
||||
for (unsigned int k = 0; k < features.size(); k++) {
|
||||
SymbolFeature &feature = features[k];
|
||||
std::vector<std::vector<Coordinate>> &geometry = feature.geometry;
|
||||
|
||||
if (!feature.label.length()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string leftKey = getKey(feature.label, geometry, false);
|
||||
std::string rightKey = getKey(feature.label, geometry, true);
|
||||
|
||||
auto left = rightIndex.find(leftKey);
|
||||
auto right = leftIndex.find(rightKey);
|
||||
|
||||
if ((left != rightIndex.end()) && (right != leftIndex.end()) && (left->second != right->second)) {
|
||||
// found lines with the same text adjacent to both ends of the current line, merge all three
|
||||
unsigned int j = mergeFromLeft(features, leftIndex, leftKey, right, geometry);
|
||||
unsigned int i = mergeFromRight(features, rightIndex, left, rightKey, features[j].geometry);
|
||||
|
||||
leftIndex.erase(leftKey);
|
||||
rightIndex.erase(rightKey);
|
||||
rightIndex[getKey(feature.label, features[i].geometry, true)] = i;
|
||||
|
||||
} else if (left != rightIndex.end()) {
|
||||
// found mergeable line adjacent to the start of the current line, merge
|
||||
mergeFromRight(features, rightIndex, left, rightKey, geometry);
|
||||
|
||||
} else if (right != leftIndex.end()) {
|
||||
// found mergeable line adjacent to the end of the current line, merge
|
||||
mergeFromLeft(features, leftIndex, leftKey, right, geometry);
|
||||
|
||||
} else {
|
||||
// no adjacent lines, add as a new item
|
||||
leftIndex[leftKey] = k;
|
||||
rightIndex[rightKey] = k;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
void mergeLines(std::vector<SymbolFeature> &features);
|
||||
|
||||
} // end namespace util
|
||||
} // end namespace mbgl
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
@@ -91,15 +90,15 @@ void Raster::bind(const GLuint custom_texture) {
|
||||
}
|
||||
|
||||
void Raster::beginFadeInTransition() {
|
||||
timestamp start = util::now();
|
||||
fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, 250_milliseconds);
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
bool Raster::needsTransition() const {
|
||||
return fade_transition != nullptr;
|
||||
}
|
||||
|
||||
void Raster::updateTransitions(timestamp now) {
|
||||
void Raster::updateTransitions(std::chrono::steady_clock::time_point now) {
|
||||
if (fade_transition->update(now) == util::transition::complete) {
|
||||
fade_transition = nullptr;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
// transitions
|
||||
void beginFadeInTransition();
|
||||
bool needsTransition() const;
|
||||
void updateTransitions(timestamp now);
|
||||
void updateTransitions(std::chrono::steady_clock::time_point now);
|
||||
|
||||
public:
|
||||
// loaded image dimensions
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
#include <mbgl/util/sqlite3.hpp>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace mapbox {
|
||||
namespace sqlite {
|
||||
|
||||
Database::Database(const std::string &filename, int flags) {
|
||||
const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr);
|
||||
if (err != SQLITE_OK) {
|
||||
Exception ex { err, sqlite3_errmsg(db) };
|
||||
db = nullptr;
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
Database::Database(Database &&other)
|
||||
: db(std::move(other.db)) {}
|
||||
|
||||
Database &Database::operator=(Database &&other) {
|
||||
std::swap(db, other.db);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Database::~Database() {
|
||||
if (db) {
|
||||
const int err = sqlite3_close(db);
|
||||
if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(db) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Database::operator bool() const {
|
||||
return db != nullptr;
|
||||
}
|
||||
|
||||
void Database::exec(const std::string &sql) {
|
||||
assert(db);
|
||||
char *msg = nullptr;
|
||||
const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg);
|
||||
if (msg) {
|
||||
Exception ex { err, msg };
|
||||
sqlite3_free(msg);
|
||||
throw ex;
|
||||
} else if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(db) };
|
||||
}
|
||||
}
|
||||
|
||||
Statement Database::prepare(const char *query) {
|
||||
assert(db);
|
||||
return std::move(Statement(db, query));
|
||||
}
|
||||
|
||||
Statement::Statement(sqlite3 *db, const char *sql) {
|
||||
const int err = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
|
||||
if (err != SQLITE_OK) {
|
||||
stmt = nullptr;
|
||||
throw Exception { err, sqlite3_errmsg(db) };
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_SQLITE_OK(err) \
|
||||
if (err != SQLITE_OK) { \
|
||||
throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; \
|
||||
}
|
||||
|
||||
Statement::Statement(Statement &&other) {
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
Statement &Statement::operator=(Statement &&other) {
|
||||
std::swap(stmt, other.stmt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Statement::~Statement() {
|
||||
if (stmt) {
|
||||
const int err = sqlite3_finalize(stmt);
|
||||
CHECK_SQLITE_OK(err)
|
||||
}
|
||||
}
|
||||
|
||||
Statement::operator bool() const {
|
||||
return stmt != nullptr;
|
||||
}
|
||||
|
||||
#define BIND_3(type, value) \
|
||||
assert(stmt); \
|
||||
const int err = sqlite3_bind_##type(stmt, offset, value); \
|
||||
CHECK_SQLITE_OK(err)
|
||||
|
||||
#define BIND_5(type, value, length, param) \
|
||||
assert(stmt); \
|
||||
const int err = sqlite3_bind_##type(stmt, offset, value, length, param); \
|
||||
CHECK_SQLITE_OK(err)
|
||||
|
||||
template <> void Statement::bind(int offset, int value) {
|
||||
BIND_3(int, value)
|
||||
}
|
||||
|
||||
template <> void Statement::bind(int offset, int64_t value) {
|
||||
BIND_3(int64, value)
|
||||
}
|
||||
|
||||
template <> void Statement::bind(int offset, double value) {
|
||||
BIND_3(double, value)
|
||||
}
|
||||
|
||||
template <> void Statement::bind(int offset, bool value) {
|
||||
BIND_3(int, value)
|
||||
}
|
||||
|
||||
template <> void Statement::bind(int offset, const char *value) {
|
||||
BIND_5(text, value, -1, nullptr)
|
||||
}
|
||||
|
||||
void Statement::bind(int offset, const std::string &value, bool retain) {
|
||||
BIND_5(blob, value.data(), int(value.size()), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)
|
||||
}
|
||||
|
||||
bool Statement::run() {
|
||||
assert(stmt);
|
||||
const int err = sqlite3_step(stmt);
|
||||
if (err == SQLITE_DONE) {
|
||||
return false;
|
||||
} else if (err == SQLITE_ROW) {
|
||||
return true;
|
||||
} else {
|
||||
throw std::runtime_error("failed to run statement");
|
||||
}
|
||||
}
|
||||
|
||||
template <> int Statement::get(int offset) {
|
||||
assert(stmt);
|
||||
return sqlite3_column_int(stmt, offset);
|
||||
}
|
||||
|
||||
template <> int64_t Statement::get(int offset) {
|
||||
assert(stmt);
|
||||
return sqlite3_column_int64(stmt, offset);
|
||||
}
|
||||
|
||||
template <> double Statement::get(int offset) {
|
||||
assert(stmt);
|
||||
return sqlite3_column_double(stmt, offset);
|
||||
}
|
||||
|
||||
template <> std::string Statement::get(int offset) {
|
||||
assert(stmt);
|
||||
return {
|
||||
reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)),
|
||||
size_t(sqlite3_column_bytes(stmt, offset))
|
||||
};
|
||||
}
|
||||
|
||||
void Statement::reset() {
|
||||
assert(stmt);
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
typedef struct sqlite3 sqlite3;
|
||||
typedef struct sqlite3_stmt sqlite3_stmt;
|
||||
|
||||
namespace mapbox {
|
||||
namespace sqlite {
|
||||
|
||||
enum OpenFlag : int {
|
||||
ReadOnly = 0x00000001,
|
||||
ReadWrite = 0x00000002,
|
||||
Create = 0x00000004,
|
||||
NoMutex = 0x00008000,
|
||||
FullMutex = 0x00010000,
|
||||
SharedCache = 0x00020000,
|
||||
PrivateCache = 0x00040000,
|
||||
};
|
||||
|
||||
struct Exception : std::runtime_error {
|
||||
inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
|
||||
const int code = 0;
|
||||
};
|
||||
|
||||
class Statement;
|
||||
|
||||
class Database {
|
||||
private:
|
||||
Database(const Database &) = delete;
|
||||
Database &operator=(const Database &) = delete;
|
||||
|
||||
public:
|
||||
Database(const std::string &filename, int flags = 0);
|
||||
Database(Database &&);
|
||||
~Database();
|
||||
Database &operator=(Database &&);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
void exec(const std::string &sql);
|
||||
Statement prepare(const char *query);
|
||||
|
||||
private:
|
||||
sqlite3 *db = nullptr;
|
||||
};
|
||||
|
||||
class Statement {
|
||||
private:
|
||||
Statement(const Statement &) = delete;
|
||||
Statement &operator=(const Statement &) = delete;
|
||||
|
||||
public:
|
||||
Statement(sqlite3 *db, const char *sql);
|
||||
Statement(Statement &&);
|
||||
~Statement();
|
||||
Statement &operator=(Statement &&);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
template <typename T> void bind(int offset, T value);
|
||||
void bind(int offset, const std::string &value, bool retain = true);
|
||||
template <typename T> T get(int offset);
|
||||
|
||||
bool run();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
sqlite3_stmt *stmt = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#ifndef DISABLE_STOPWATCH
|
||||
#include <mbgl/util/stopwatch.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
@@ -10,21 +9,21 @@
|
||||
using namespace mbgl::util;
|
||||
|
||||
stopwatch::stopwatch(Event event_)
|
||||
: event(event_), start(now()) {}
|
||||
: event(event_), start(std::chrono::steady_clock::now()) {}
|
||||
|
||||
stopwatch::stopwatch(EventSeverity severity_, Event event_)
|
||||
: severity(severity_), event(event_), start(now()) {}
|
||||
: severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {}
|
||||
|
||||
stopwatch::stopwatch(const std::string &name_, Event event_)
|
||||
: name(name_), event(event_), start(now()) {}
|
||||
: name(name_), event(event_), start(std::chrono::steady_clock::now()) {}
|
||||
|
||||
stopwatch::stopwatch(const std::string &name_, EventSeverity severity_, Event event_)
|
||||
: name(name_), severity(severity_), event(event_), start(now()) {}
|
||||
: name(name_), severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {}
|
||||
|
||||
void stopwatch::report(const std::string &name_) {
|
||||
timestamp duration = now() - start;
|
||||
std::chrono::steady_clock::duration duration = std::chrono::steady_clock::now() - start;
|
||||
|
||||
Log::Record(severity, event, name_ + ": " + util::toString(double(duration) / 1_millisecond) + "ms");
|
||||
Log::Record(severity, event, name_ + ": " + util::toString(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()) + "ms");
|
||||
start += duration;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/platform/event.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
@@ -22,7 +23,7 @@ private:
|
||||
const std::string name;
|
||||
EventSeverity severity = EventSeverity::Debug;
|
||||
Event event = Event::General;
|
||||
uint64_t start;
|
||||
std::chrono::steady_clock::time_point start;
|
||||
};
|
||||
#else
|
||||
class stopwatch {
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
timestamp now() {
|
||||
return uv_hrtime();
|
||||
}
|
||||
|
||||
static const char *week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
@@ -10,7 +10,7 @@ UnitBezier ease(0, 0, 0.25, 1);
|
||||
transition::~transition() {}
|
||||
|
||||
template <typename T>
|
||||
transition::state ease_transition<T>::update(timestamp now) const {
|
||||
transition::state ease_transition<T>::update(std::chrono::steady_clock::time_point now) const {
|
||||
float t = progress(now);
|
||||
if (t >= 1) {
|
||||
value = to;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define MBGL_UTIL_TRANSITION
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <chrono>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
@@ -14,34 +14,35 @@ public:
|
||||
complete
|
||||
};
|
||||
|
||||
inline transition(timestamp start_, timestamp duration_)
|
||||
inline transition(std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_)
|
||||
: start(start_),
|
||||
duration(duration_) {}
|
||||
|
||||
inline float progress(timestamp now) const {
|
||||
if (duration == 0) return 1;
|
||||
inline float progress(std::chrono::steady_clock::time_point now) const {
|
||||
if (duration == std::chrono::steady_clock::duration::zero()) return 1;
|
||||
if (start > now) return 0;
|
||||
|
||||
return (float)(now - start) / duration;
|
||||
return std::chrono::duration<float>(now - start) / duration;
|
||||
}
|
||||
|
||||
virtual state update(timestamp now) const = 0;
|
||||
virtual state update(std::chrono::steady_clock::time_point now) const = 0;
|
||||
virtual ~transition();
|
||||
|
||||
protected:
|
||||
const timestamp start, duration;
|
||||
const std::chrono::steady_clock::time_point start;
|
||||
const std::chrono::steady_clock::duration duration;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ease_transition : public transition {
|
||||
public:
|
||||
ease_transition(T from_, T to_, T& value_, timestamp start_, timestamp duration_)
|
||||
ease_transition(T from_, T to_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_)
|
||||
: transition(start_, duration_),
|
||||
from(from_),
|
||||
to(to_),
|
||||
value(value_) {}
|
||||
|
||||
state update(timestamp now) const;
|
||||
state update(std::chrono::steady_clock::time_point now) const;
|
||||
|
||||
private:
|
||||
const T from, to;
|
||||
@@ -52,12 +53,12 @@ private:
|
||||
template <typename T>
|
||||
class timeout : public transition {
|
||||
public:
|
||||
timeout(T final_value_, T& value_, timestamp start_, timestamp duration_)
|
||||
timeout(T final_value_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_)
|
||||
: transition(start_, duration_),
|
||||
final_value(final_value_),
|
||||
value(value_) {}
|
||||
|
||||
state update(timestamp now) const {
|
||||
state update(std::chrono::steady_clock::time_point now) const {
|
||||
if (progress(now) >= 1) {
|
||||
value = final_value;
|
||||
return complete;
|
||||
|
||||
@@ -25,7 +25,7 @@ void uv__worker_free_messenger(uv_messenger_t *msgr) {
|
||||
void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) {
|
||||
uv_worker_t *worker = worker_thread->worker;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
assert(uv_thread_self() == worker->thread_id);
|
||||
#endif
|
||||
|
||||
@@ -101,7 +101,7 @@ void uv__worker_thread_loop(void *ptr) {
|
||||
}
|
||||
|
||||
int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
worker->thread_id = uv_thread_self();
|
||||
#endif
|
||||
worker->loop = loop;
|
||||
@@ -134,7 +134,7 @@ int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *
|
||||
|
||||
void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
|
||||
uv_worker_after_cb after_work_cb) {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
assert(uv_thread_self() == worker->thread_id);
|
||||
#endif
|
||||
|
||||
@@ -155,7 +155,7 @@ void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
|
||||
}
|
||||
|
||||
void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
assert(uv_thread_self() == worker->thread_id);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ typedef void (*uv_worker_after_cb)(void *data);
|
||||
typedef void (*uv_worker_close_cb)(uv_worker_t *worker);
|
||||
|
||||
struct uv_worker_s {
|
||||
#ifndef NDEBUG
|
||||
#ifdef DEBUG
|
||||
unsigned long thread_id;
|
||||
#endif
|
||||
uv_loop_t *loop;
|
||||
|
||||
@@ -22,4 +22,12 @@ std::string cwd() {
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *getFileRequestError(uv_fs_t *req) {
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
return uv_strerror(uv_last_error(req->loop));
|
||||
#else
|
||||
return uv_strerror(int(req->result));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user