diff options
Diffstat (limited to 'src')
42 files changed, 752 insertions, 550 deletions
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp index bb230e4efd..067c5a4062 100644 --- a/src/mbgl/geometry/glyph_atlas.cpp +++ b/src/mbgl/geometry/glyph_atlas.cpp @@ -1,11 +1,11 @@ #include <mbgl/geometry/glyph_atlas.hpp> +#include <mbgl/text/font_stack.hpp> + #include <mbgl/platform/gl.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> -#include <mbgl/util/std.hpp> - #include <cassert> #include <algorithm> @@ -16,7 +16,7 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_) : width(width_), height(height_), bin(width_, height_), - data(util::make_unique<uint8_t[]>(width_ * height_)), + data(std::make_unique<uint8_t[]>(width_ * height_)), dirty(true) { } @@ -69,8 +69,9 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, uint16_t buffered_height = glyph.metrics.height + buffer * 2; // Add a 1px border around every image. - uint16_t pack_width = buffered_width; - uint16_t pack_height = buffered_height; + const uint16_t padding = 1; + uint16_t pack_width = buffered_width + 2 * padding; + uint16_t pack_height = buffered_height + 2 * padding; // Increase to next number divisible by 4, but at least 1. // This is so we can scale down the texture coordinates and pack them @@ -92,7 +93,7 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, // Copy the bitmap const uint8_t* source = reinterpret_cast<const uint8_t*>(glyph.bitmap.data()); for (uint32_t y = 0; y < buffered_height; y++) { - uint32_t y1 = width * (rect.y + y) + rect.x; + uint32_t y1 = width * (rect.y + y + padding) + rect.x + padding; uint32_t y2 = buffered_width * y; for (uint32_t x = 0; x < buffered_width; x++) { data[y1 + x] = source[y2 + x]; diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp index 91ac15639b..dc5ee0901a 100644 --- a/src/mbgl/geometry/line_atlas.cpp +++ b/src/mbgl/geometry/line_atlas.cpp @@ -3,7 +3,6 @@ #include <mbgl/platform/gl.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> -#include <mbgl/util/std.hpp> #include <boost/functional/hash.hpp> @@ -15,7 +14,7 @@ using namespace mbgl; LineAtlas::LineAtlas(uint16_t w, uint16_t h) : width(w), height(h), - data(util::make_unique<uint8_t[]>(w * h)), + data(std::make_unique<uint8_t[]>(w * h)), dirty(true) { } diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 5d47793cf5..93bc76aa5c 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -127,10 +127,12 @@ SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repea rect.h = pos.height / pos.pixelRatio; } + const float padding = 1; + return SpriteAtlasPosition { {{ float(rect.w), float(rect.h) }}, - {{ float(rect.x) / width, float(rect.y) / height }}, - {{ float(rect.x + rect.w) / width, float(rect.y + rect.h) / height }} + {{ float(rect.x + padding) / width, float(rect.y + padding) / height }}, + {{ float(rect.x + padding + rect.w) / width, float(rect.y + padding + rect.h) / height }} }; } @@ -138,7 +140,7 @@ void SpriteAtlas::allocate() { if (!data) { dimension w = static_cast<dimension>(width * pixelRatio); dimension h = static_cast<dimension>(height * pixelRatio); - data = util::make_unique<uint32_t[]>(w * h); + data = std::make_unique<uint32_t[]>(w * h); std::fill(data.get(), data.get() + w * h, 0); } } @@ -151,12 +153,14 @@ void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, co const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() }; const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height }; + const int offset = 1; + allocate(); uint32_t *const dstData = data.get(); const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio), static_cast<unsigned int>(height * pixelRatio) }; - const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio), - static_cast<uint32_t>(dst.y * pixelRatio), + const Rect<uint32_t> dstPos { static_cast<uint32_t>((offset + dst.x) * pixelRatio), + static_cast<uint32_t>((offset + dst.y) * pixelRatio), static_cast<uint32_t>(dst.originalW * pixelRatio), static_cast<uint32_t>(dst.originalH * pixelRatio) }; @@ -295,6 +299,8 @@ void SpriteAtlas::bind(bool linear) { SpriteAtlas::~SpriteAtlas() { std::lock_guard<std::recursive_mutex> lock(mtx); - Environment::Get().abandonTexture(texture); - texture = 0; + if (texture) { + Environment::Get().abandonTexture(texture); + texture = 0; + } } diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 9d21b4e48b..6df49ec647 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -5,7 +5,6 @@ #include <mbgl/map/map_data.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/ptr.hpp> -#include <mbgl/util/std.hpp> #include <algorithm> #include <memory> @@ -110,7 +109,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points, // track the annotation global ID and its geometry auto anno_it = annotations.emplace( annotationID, - util::make_unique<Annotation>(AnnotationType::Point, + std::make_unique<Annotation>(AnnotationType::Point, AnnotationSegments({ { points[i] } }))); const uint8_t maxZoom = data.transform.getMaxZoom(); @@ -162,7 +161,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points, // create tile & record annotation association auto tile_pos = tiles.emplace( tileID, std::make_pair(std::unordered_set<uint32_t>({ annotationID }), - util::make_unique<LiveTile>())); + std::make_unique<LiveTile>())); // add point layer to tile tile_pos.first->second.second->addLayer(layerID, layer); } diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index a80b03226f..0c9a078e57 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -4,7 +4,6 @@ #include <mbgl/map/tile_id.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/vec.hpp> #include <string> diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 3ba82252de..0479189300 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -3,15 +3,14 @@ #include <mbgl/map/view.hpp> #include <mbgl/map/map_data.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/projection.hpp> #include <mbgl/util/thread.hpp> namespace mbgl { Map::Map(View& view, FileSource& fileSource, MapMode mode) - : data(util::make_unique<MapData>(view, mode)), - context(util::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data)) + : data(std::make_unique<MapData>(view, mode)), + context(std::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data)) { view.initialize(this); } @@ -195,17 +194,6 @@ void Map::resetNorth() { } -#pragma mark - Access Token - -void Map::setAccessToken(const std::string &token) { - data->setAccessToken(token); -} - -std::string Map::getAccessToken() const { - return data->getAccessToken(); -} - - #pragma mark - Projection void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const { diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index f373274ebd..c8948097f7 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -20,11 +20,9 @@ #include <mbgl/style/style.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/worker.hpp> #include <mbgl/util/texture_pool.hpp> -#include <mbgl/util/mapbox.hpp> #include <mbgl/util/exception.hpp> namespace mbgl { @@ -35,12 +33,12 @@ MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, Map env(fileSource), envScope(env, ThreadType::Map, "Map"), updated(static_cast<UpdateType>(Update::Nothing)), - asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })), - glyphStore(util::make_unique<GlyphStore>(loop, env)), - glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)), - spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)), - lineAtlas(util::make_unique<LineAtlas>(512, 512)), - texturePool(util::make_unique<TexturePool>()) { + asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })), + glyphStore(std::make_unique<GlyphStore>(loop, env)), + glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)), + spriteAtlas(std::make_unique<SpriteAtlas>(512, 512)), + lineAtlas(std::make_unique<LineAtlas>(512, 512)), + texturePool(std::make_unique<TexturePool>()) { assert(Environment::currentlyOn(ThreadType::Map)); asyncUpdate->unref(); @@ -90,7 +88,7 @@ void MapContext::triggerUpdate(const Update u) { } void MapContext::setStyleURL(const std::string& url) { - styleURL = mbgl::util::mapbox::normalizeStyleURL(url, data.getAccessToken()); + styleURL = url; styleJSON.clear(); const size_t pos = styleURL.rfind('/'); @@ -99,7 +97,7 @@ void MapContext::setStyleURL(const std::string& url) { base = styleURL.substr(0, pos + 1); } - env.request({ Resource::Kind::JSON, styleURL }, [this, base](const Response &res) { + env.request({ Resource::Kind::Style, styleURL }, [this, base](const Response &res) { if (res.status == Response::Successful) { loadStyleJSON(res.data, base); } else { @@ -121,17 +119,15 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) resourceLoader.reset(); style.reset(); - style = util::make_unique<Style>(); + style = std::make_unique<Style>(); style->base = base; style->loadJSON((const uint8_t *)json.c_str()); style->cascade(data.getClasses()); style->setDefaultTransitionDuration(data.getDefaultTransitionDuration()); - const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken()); - glyphStore->setURL(glyphURL); + glyphStore->setURL(style->glyph_url); - resourceLoader = util::make_unique<ResourceLoader>(); - resourceLoader->setAccessToken(data.getAccessToken()); + resourceLoader = std::make_unique<ResourceLoader>(); resourceLoader->setObserver(this); resourceLoader->setStyle(style.get()); resourceLoader->setGlyphStore(glyphStore.get()); @@ -224,7 +220,7 @@ void MapContext::render() { assert(style); if (!painter) { - painter = util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas); + painter = std::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas); painter->setup(); } @@ -232,7 +228,7 @@ void MapContext::render() { painter->render(*style, transformState, data.getAnimationTime()); if (data.mode == MapMode::Still) { - callback(view.readStillImage()); + callback(nullptr, view.readStillImage()); callback = nullptr; } @@ -274,4 +270,13 @@ void MapContext::onTileDataChanged() { triggerUpdate(); } +void MapContext::onResourceLoadingFailed(std::exception_ptr error) { + assert(Environment::currentlyOn(ThreadType::Map)); + + if (data.mode == MapMode::Still && callback) { + callback(error, nullptr); + callback = nullptr; + } +} + } diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index c842454849..fb9fdb4d4b 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -43,7 +43,7 @@ public: void resize(uint16_t width, uint16_t height, float ratio); - using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>; + using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>; void renderStill(StillImageCallback callback); void triggerUpdate(Update = Update::Nothing); @@ -61,6 +61,7 @@ public: // ResourceLoader::Observer implementation. void onTileDataChanged() override; + void onResourceLoadingFailed(std::exception_ptr error) override; private: void updateTiles(); diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp index 803a9b1acb..32722d07e8 100644 --- a/src/mbgl/map/map_data.hpp +++ b/src/mbgl/map/map_data.hpp @@ -27,15 +27,6 @@ public: setDefaultTransitionDuration(Duration::zero()); } - inline std::string getAccessToken() const { - Lock lock(mtx); - return accessToken; - } - inline void setAccessToken(const std::string &token) { - Lock lock(mtx); - accessToken = token; - } - // Adds the class if it's not yet set. Returns true when it added the class, and false when it // was already present. bool addClass(const std::string& klass); @@ -105,7 +96,6 @@ public: private: mutable std::mutex mtx; - std::string accessToken; std::vector<std::string> classes; std::atomic<uint8_t> debug { false }; std::atomic<uint8_t> collisionDebug { false }; diff --git a/src/mbgl/map/resource_loader.cpp b/src/mbgl/map/resource_loader.cpp index 2be86dc5e2..8e054c0d82 100644 --- a/src/mbgl/map/resource_loader.cpp +++ b/src/mbgl/map/resource_loader.cpp @@ -45,7 +45,7 @@ void ResourceLoader::setStyle(Style* style) { for (const auto& source : style->sources) { source->setObserver(this); - source->load(accessToken_); + source->load(); } } @@ -60,11 +60,6 @@ void ResourceLoader::setGlyphStore(GlyphStore* glyphStore) { glyphStore_->setObserver(this); } - -void ResourceLoader::setAccessToken(const std::string& accessToken) { - accessToken_ = accessToken; -} - void ResourceLoader::update(MapData& data, const TransformState& transform, GlyphAtlas& glyphAtlas, @@ -76,7 +71,7 @@ void ResourceLoader::update(MapData& data, const float pixelRatio = transform.getPixelRatio(); if (!sprite_ || !sprite_->hasPixelRatio(pixelRatio)) { - sprite_ = util::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio); + sprite_ = std::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio); sprite_->setObserver(this); spriteAtlas.resize(pixelRatio); @@ -104,10 +99,18 @@ void ResourceLoader::onGlyphRangeLoaded() { emitTileDataChanged(); } +void ResourceLoader::onGlyphRangeLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + void ResourceLoader::onSourceLoaded() { emitTileDataChanged(); } +void ResourceLoader::onSourceLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + void ResourceLoader::onTileLoaded(bool isNewTile) { if (isNewTile) { shouldReparsePartialTiles_ = true; @@ -116,12 +119,20 @@ void ResourceLoader::onTileLoaded(bool isNewTile) { emitTileDataChanged(); } +void ResourceLoader::onTileLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + void ResourceLoader::onSpriteLoaded() { shouldReparsePartialTiles_ = true; emitTileDataChanged(); } +void ResourceLoader::onSpriteLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + void ResourceLoader::emitTileDataChanged() { assert(Environment::currentlyOn(ThreadType::Map)); @@ -130,4 +141,20 @@ void ResourceLoader::emitTileDataChanged() { } } +void ResourceLoader::emitResourceLoadingFailed(std::exception_ptr error) { + assert(Environment::currentlyOn(ThreadType::Map)); + + try { + if (error) { + std::rethrow_exception(error); + } + } catch(const std::exception& e) { + Log::Error(Event::ResourceLoader, e.what()); + } + + if (observer_) { + observer_->onResourceLoadingFailed(error); + } +} + } diff --git a/src/mbgl/map/resource_loader.hpp b/src/mbgl/map/resource_loader.hpp index 379444135e..525e4653a0 100644 --- a/src/mbgl/map/resource_loader.hpp +++ b/src/mbgl/map/resource_loader.hpp @@ -32,6 +32,7 @@ public: virtual ~Observer() = default; virtual void onTileDataChanged() = 0; + virtual void onResourceLoadingFailed(std::exception_ptr error) = 0; }; ResourceLoader(); @@ -48,9 +49,6 @@ public: // style. void setGlyphStore(GlyphStore* glyphStore); - // Set the access token to be used for loading the tile data. - void setAccessToken(const std::string& accessToken); - // Fetch the tiles needed by the current viewport and emit a signal when // a tile is ready so observers can render the tile. void update(MapData&, const TransformState&, GlyphAtlas&, SpriteAtlas&, TexturePool&); @@ -62,20 +60,24 @@ public: // GlyphStore::Observer implementation. void onGlyphRangeLoaded() override; + void onGlyphRangeLoadingFailed(std::exception_ptr error) override; // Source::Observer implementation. void onSourceLoaded() override; + void onSourceLoadingFailed(std::exception_ptr error) override; void onTileLoaded(bool isNewTile) override; + void onTileLoadingFailed(std::exception_ptr error) override; // Sprite::Observer implementation. void onSpriteLoaded() override; + void onSpriteLoadingFailed(std::exception_ptr error) override; private: void emitTileDataChanged(); + void emitResourceLoadingFailed(std::exception_ptr error); bool shouldReparsePartialTiles_ = false; - std::string accessToken_; util::ptr<Sprite> sprite_; GlyphStore* glyphStore_ = nullptr; diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 331e48436f..b6838a4713 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -4,6 +4,7 @@ #include <mbgl/map/transform.hpp> #include <mbgl/map/tile.hpp> #include <mbgl/renderer/painter.hpp> +#include <mbgl/util/exception.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> @@ -13,6 +14,7 @@ #include <mbgl/style/style_layer.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/util/uv_detail.hpp> +#include <mbgl/util/std.hpp> #include <mbgl/util/token.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/tile_cover.hpp> @@ -139,18 +141,19 @@ bool Source::isLoaded() const { // Note: This is a separate function that must be called exactly once after creation // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. -void Source::load(const std::string& accessToken) { +void Source::load() { if (info.url.empty()) { loaded = true; return; } - const std::string url = util::mapbox::normalizeSourceURL(info.url, accessToken); - req = Environment::Get().request({ Resource::Kind::JSON, url }, [this](const Response &res) { + req = Environment::Get().request({ Resource::Kind::Source, info.url }, [this](const Response &res) { req = nullptr; if (res.status != Response::Successful) { - Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str()); + std::stringstream message; + message << "Failed to load [" << info.url << "]: " << res.message; + emitSourceLoadingFailed(message.str()); return; } @@ -158,7 +161,9 @@ void Source::load(const std::string& accessToken) { d.Parse<0>(res.data.c_str()); if (d.HasParseError()) { - Log::Error(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError()); + std::stringstream message; + message << "Failed to parse [" << info.url << "]: " << d.GetErrorOffset() << " - " << d.GetParseError(); + emitSourceLoadingFailed(message.str()); return; } @@ -261,7 +266,8 @@ TileData::State Source::addTile(MapData& data, } const float overscaling = id.z > info.max_zoom ? std::pow(2.0f, id.z - info.max_zoom) : 1.0f; - auto pos = tiles.emplace(id, util::make_unique<Tile>(id));//, util::tileSize * overscaling)); + auto pos = tiles.emplace(id, std::make_unique<Tile>(id)); + Tile& new_tile = *pos.first->second; // We couldn't find the tile in the list. Create a new one. @@ -283,8 +289,9 @@ TileData::State Source::addTile(MapData& data, new_tile.data = cache.get(normalized_id.to_uint64()); } - auto callback = std::bind(&Source::emitTileLoaded, this, true); if (!new_tile.data) { + auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id); + // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { new_tile.data = @@ -294,7 +301,8 @@ TileData::State Source::addTile(MapData& data, new_tile.data->request(style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Raster) { new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info); - new_tile.data->request(style.workers, transformState.getPixelRatio(), callback); + new_tile.data->request( + style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Annotations) { new_tile.data = std::make_shared<LiveTileData>(normalized_id, data.annotationManager, style, glyphAtlas, @@ -546,16 +554,53 @@ void Source::setObserver(Observer* observer) { observer_ = observer; } +void Source::tileLoadingCompleteCallback(const TileID& normalized_id) { + auto it = tile_data.find(normalized_id); + if (it == tile_data.end()) { + return; + } + + util::ptr<TileData> data = it->second.lock(); + if (!data) { + return; + } + + if (data->getState() == TileData::State::obsolete && !data->getError().empty()) { + emitTileLoadingFailed(data->getError()); + return; + } + + emitTileLoaded(true); +} + void Source::emitSourceLoaded() { if (observer_) { observer_->onSourceLoaded(); } } +void Source::emitSourceLoadingFailed(const std::string& message) { + if (!observer_) { + return; + } + + auto error = std::make_exception_ptr(util::SourceLoadingException(message)); + observer_->onSourceLoadingFailed(error); +} + void Source::emitTileLoaded(bool isNewTile) { if (observer_) { observer_->onTileLoaded(isNewTile); } } +void Source::emitTileLoadingFailed(const std::string& message) { + if (!observer_) { + return; + } + + auto error = std::make_exception_ptr(util::TileLoadingException(message)); + observer_->onTileLoadingFailed(error); +} + } diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 505292cc74..bf49bd69ea 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -58,13 +58,16 @@ public: virtual ~Observer() = default; virtual void onSourceLoaded() = 0; + virtual void onSourceLoadingFailed(std::exception_ptr error) = 0; + virtual void onTileLoaded(bool isNewTile) = 0; + virtual void onTileLoadingFailed(std::exception_ptr error) = 0; }; Source(); ~Source(); - void load(const std::string& accessToken); + void load(); bool isLoaded() const; void load(MapData&, Environment&, std::function<void()> callback); @@ -102,8 +105,12 @@ public: private: void redoPlacement(const TransformState& transformState, bool collisionDebug); + void tileLoadingCompleteCallback(const TileID& normalized_id); + void emitSourceLoaded(); + void emitSourceLoadingFailed(const std::string& message); void emitTileLoaded(bool isNewTile); + void emitTileLoadingFailed(const std::string& message); bool handlePartialTile(const TileID &id, Worker &worker); bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain); diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp index cfc0974571..d5af3e1d1d 100644 --- a/src/mbgl/map/sprite.cpp +++ b/src/mbgl/map/sprite.cpp @@ -1,17 +1,19 @@ #include <mbgl/map/sprite.hpp> -#include <mbgl/util/raster.hpp> -#include <mbgl/platform/log.hpp> -#include <string> -#include <mbgl/platform/platform.hpp> #include <mbgl/map/environment.hpp> +#include <mbgl/platform/log.hpp> +#include <mbgl/platform/platform.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/util/raster.hpp> #include <mbgl/util/uv_detail.hpp> -#include <mbgl/util/std.hpp> #include <rapidjson/document.h> +#include <string> +#include <sstream> + using namespace mbgl; SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16_t height_, float pixelRatio_, bool sdf_) @@ -39,27 +41,31 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_) std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png"); std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json"); - jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this](const Response &res) { + jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this, jsonURL](const Response &res) { jsonRequest = nullptr; if (res.status == Response::Successful) { body = res.data; - parseJSON(); + parseJSON(jsonURL); } else { - Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str()); + std::stringstream message; + message << "Failed to load [" << jsonURL << "]: " << res.message; + emitSpriteLoadingFailed(message.str()); + return; } - loadedJSON = true; emitSpriteLoadedIfComplete(); }); - spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) { + spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this, spriteURL](const Response &res) { spriteRequest = nullptr; if (res.status == Response::Successful) { image = res.data; - parseImage(); + parseImage(spriteURL); } else { - Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str()); + std::stringstream message; + message << "Failed to load [" << spriteURL << "]: " << res.message; + emitSpriteLoadingFailed(message.str()); + return; } - loadedImage = true; emitSpriteLoadedIfComplete(); }); } @@ -80,6 +86,15 @@ void Sprite::emitSpriteLoadedIfComplete() { } } +void Sprite::emitSpriteLoadingFailed(const std::string& message) { + if (!observer) { + return; + } + + auto error = std::make_exception_ptr(util::SpriteLoadingException(message)); + observer->onSpriteLoadingFailed(error); +} + bool Sprite::isLoaded() const { return loadedImage && loadedJSON; } @@ -88,21 +103,29 @@ bool Sprite::hasPixelRatio(float ratio) const { return pixelRatio == (ratio > 1 ? 2 : 1); } -void Sprite::parseImage() { - raster = util::make_unique<util::Image>(image); +void Sprite::parseImage(const std::string& spriteURL) { + raster = std::make_unique<util::Image>(image); if (!*raster) { raster.reset(); + std::stringstream message; + message << "Failed to parse [" << spriteURL << "]"; + emitSpriteLoadingFailed(message.str()); + return; } + image.clear(); + loadedImage = true; } -void Sprite::parseJSON() { +void Sprite::parseJSON(const std::string& jsonURL) { rapidjson::Document d; d.Parse<0>(body.c_str()); body.clear(); if (d.HasParseError()) { - Log::Warning(Event::Sprite, "sprite JSON is invalid"); + std::stringstream message; + message << "Failed to parse [" << jsonURL << "]: " << d.GetErrorOffset() << " - " << d.GetParseError(); + emitSpriteLoadingFailed(message.str()); } else if (d.IsObject()) { for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) { const std::string& name = itr->name.GetString(); @@ -125,8 +148,11 @@ void Sprite::parseJSON() { pos.emplace(name, SpritePosition { x, y, width, height, spritePixelRatio, sdf }); } } + loadedJSON = true; } else { - Log::Warning(Event::Sprite, "sprite JSON root is not an object"); + std::stringstream message; + message << "Failed to parse [" << jsonURL << "]: Root is not an object"; + emitSpriteLoadingFailed(message.str()); } } diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp index ccf2561618..b0fda30018 100644 --- a/src/mbgl/map/sprite.hpp +++ b/src/mbgl/map/sprite.hpp @@ -39,6 +39,7 @@ public: virtual ~Observer() = default; virtual void onSpriteLoaded() = 0; + virtual void onSpriteLoadingFailed(std::exception_ptr error) = 0; }; Sprite(const std::string& baseUrl, float pixelRatio); @@ -56,9 +57,10 @@ public: void setObserver(Observer* observer); private: void emitSpriteLoadedIfComplete(); + void emitSpriteLoadingFailed(const std::string& message); - void parseJSON(); - void parseImage(); + void parseJSON(const std::string& jsonURL); + void parseImage(const std::string& spriteURL); std::string body; std::string image; diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index 83488ecb9b..6ff92bb6e5 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -1,12 +1,14 @@ #include <mbgl/map/tile_data.hpp> + #include <mbgl/map/environment.hpp> #include <mbgl/map/source.hpp> #include <mbgl/map/transform_state.hpp> - +#include <mbgl/platform/log.hpp> #include <mbgl/storage/file_source.hpp> -#include <mbgl/util/worker.hpp> #include <mbgl/util/work_request.hpp> -#include <mbgl/platform/log.hpp> +#include <mbgl/util/worker.hpp> + +#include <sstream> using namespace mbgl; @@ -35,7 +37,9 @@ void TileData::setState(const State& state_) { state = state_; } -void TileData::request(Worker& worker, float pixelRatio, std::function<void()> callback) { +void TileData::request(Worker& worker, + float pixelRatio, + const std::function<void()>& callback) { std::string url = source.tileURL(id, pixelRatio); state = State::loading; @@ -43,7 +47,10 @@ void TileData::request(Worker& worker, float pixelRatio, std::function<void()> c req = nullptr; if (res.status != Response::Successful) { - Log::Error(Event::HttpRequest, "[%s] tile loading failed: %s", url.c_str(), res.message.c_str()); + std::stringstream message; + message << "Failed to load [" << url << "]: " << res.message; + setError(message.str()); + callback(); return; } @@ -82,3 +89,8 @@ bool TileData::reparse(Worker& worker, std::function<void()> callback) { workRequest = worker.send([this] { parse(); endParsing(); }, callback); return true; } + +void TileData::setError(const std::string& message) { + error = message; + setState(State::obsolete); +} diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 1ec58ec839..2b237a61cb 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -45,7 +45,7 @@ public: TileData(const TileID&, const SourceInfo&); ~TileData(); - void request(Worker&, float pixelRatio, std::function<void ()> callback); + void request(Worker&, float pixelRatio, const std::function<void()>& callback); // Schedule a tile reparse on a worker thread and call the callback on // completion. It will return true if the work was schedule or false it was @@ -69,13 +69,20 @@ public: // We let subclasses override setState() so they // can intercept the state change and react accordingly. virtual void setState(const State& state); - inline State getState() const { return state; } void endParsing(); + // Error message to be set in case of request + // and parsing errors. + void setError(const std::string& message); + + std::string getError() const { + return error; + } + // Override this in the child class. virtual void parse() = 0; virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0; @@ -106,6 +113,8 @@ protected: private: std::atomic<State> state; + std::string error; + protected: // Contains the tile ID string for painting debug information. DebugFontBuffer debugFontBuffer; diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index eb9a7c2e9f..61af227034 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -8,7 +8,6 @@ #include <mbgl/renderer/line_bucket.hpp> #include <mbgl/renderer/symbol_bucket.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/style/style.hpp> #include <locale> @@ -154,7 +153,7 @@ void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& la std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& layer, const StyleBucket& bucket_desc) { - auto bucket = util::make_unique<FillBucket>(tile.fillVertexBuffer, + auto bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer); addBucketGeometries(bucket, layer, bucket_desc.filter); @@ -163,7 +162,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer, const StyleBucket& bucket_desc) { - auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer, + auto bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer, tile.triangleElementsBuffer); const float z = tile.id.z; @@ -180,7 +179,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer, const StyleBucket& bucket_desc) { - auto bucket = util::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling); + auto bucket = std::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling); const float z = tile.id.z; auto& layout = bucket->layout; diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 32127fb26f..182e534295 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -2,7 +2,6 @@ #include <mbgl/map/view.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mat4.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/unitbezier.hpp> #include <mbgl/util/interpolate.hpp> @@ -384,6 +383,7 @@ void Transform::_setAngle(double new_angle, const Duration duration) { MapChangeRegionWillChange); final.angle = _normalizeAngle(new_angle, current.angle); + current.angle = _normalizeAngle(current.angle, final.angle); if (duration == Duration::zero()) { current.angle = final.angle; diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index fb64c224cf..4cb657038d 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -1,6 +1,5 @@ #include <mbgl/map/vector_tile_data.hpp> #include <mbgl/map/tile_parser.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_bucket.hpp> #include <mbgl/map/source.hpp> @@ -31,7 +30,7 @@ VectorTileData::VectorTileData(const TileID& id_, sprite(sprite_), style(style_), overscaling(overscaling_), - collision(util::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)), + collision(std::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)), lastAngle(angle), currentAngle(angle) { } @@ -68,9 +67,9 @@ void VectorTileData::parse() { redoPlacement(); } } catch (const std::exception& ex) { - Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); - setState(State::obsolete); - return; + std::stringstream message; + message << "Failed to parse [" << int(id.z) << "/" << id.x << "/" << id.y << "]: " << ex.what(); + setError(message.str()); } } diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp index c59b0970e0..a98a8b7cdf 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/fill_bucket.cpp @@ -7,7 +7,6 @@ #include <mbgl/shader/plain_shader.hpp> #include <mbgl/shader/pattern_shader.hpp> #include <mbgl/shader/outline_shader.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/platform/gl.hpp> #include <mbgl/platform/log.hpp> @@ -107,7 +106,7 @@ void FillBucket::tessellate() { 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(util::make_unique<LineGroup>()); + lineGroups.emplace_back(std::make_unique<LineGroup>()); } assert(lineGroups.back()); @@ -154,7 +153,7 @@ void FillBucket::tessellate() { 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(util::make_unique<TriangleGroup>()); + triangleGroups.emplace_back(std::make_unique<TriangleGroup>()); } // We're generating triangle fans, so we always start with the first diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index bbfc02ead1..f696874afe 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -8,7 +8,6 @@ #include <mbgl/shader/linesdf_shader.hpp> #include <mbgl/shader/linepattern_shader.hpp> #include <mbgl/util/math.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/platform/gl.hpp> #ifndef BUFFER_OFFSET @@ -290,7 +289,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertexCount > 65535)) { // Move to a new group because the old one can't hold the geometry. - triangleGroups.emplace_back(util::make_unique<TriangleGroup>()); + triangleGroups.emplace_back(std::make_unique<TriangleGroup>()); } assert(triangleGroups.back()); diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 722a520027..977b6e565f 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -27,7 +27,6 @@ #include <mbgl/shader/gaussian_shader.hpp> #include <mbgl/shader/box_shader.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mat3.hpp> @@ -95,19 +94,19 @@ void Painter::setup() { } void Painter::setupShaders() { - if (!plainShader) plainShader = util::make_unique<PlainShader>(); - if (!outlineShader) outlineShader = util::make_unique<OutlineShader>(); - if (!lineShader) lineShader = util::make_unique<LineShader>(); - if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>(); - if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>(); - if (!patternShader) patternShader = util::make_unique<PatternShader>(); - if (!iconShader) iconShader = util::make_unique<IconShader>(); - if (!rasterShader) rasterShader = util::make_unique<RasterShader>(); - if (!sdfGlyphShader) sdfGlyphShader = util::make_unique<SDFGlyphShader>(); - if (!sdfIconShader) sdfIconShader = util::make_unique<SDFIconShader>(); - if (!dotShader) dotShader = util::make_unique<DotShader>(); - if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>(); - if (!collisionBoxShader) collisionBoxShader = util::make_unique<CollisionBoxShader>(); + if (!plainShader) plainShader = std::make_unique<PlainShader>(); + if (!outlineShader) outlineShader = std::make_unique<OutlineShader>(); + if (!lineShader) lineShader = std::make_unique<LineShader>(); + if (!linesdfShader) linesdfShader = std::make_unique<LineSDFShader>(); + if (!linepatternShader) linepatternShader = std::make_unique<LinepatternShader>(); + if (!patternShader) patternShader = std::make_unique<PatternShader>(); + if (!iconShader) iconShader = std::make_unique<IconShader>(); + if (!rasterShader) rasterShader = std::make_unique<RasterShader>(); + if (!sdfGlyphShader) sdfGlyphShader = std::make_unique<SDFGlyphShader>(); + if (!sdfIconShader) sdfIconShader = std::make_unique<SDFIconShader>(); + if (!dotShader) dotShader = std::make_unique<DotShader>(); + if (!gaussianShader) gaussianShader = std::make_unique<GaussianShader>(); + if (!collisionBoxShader) collisionBoxShader = std::make_unique<CollisionBoxShader>(); } void Painter::resize() { diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 649bd46794..b2fdafadbd 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -9,7 +9,6 @@ #include <mbgl/shader/outline_shader.hpp> #include <mbgl/shader/pattern_shader.hpp> #include <mbgl/shader/plain_shader.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/mat3.hpp> using namespace mbgl; diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index 61aff9c1a8..2a8f8e7078 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -3,7 +3,6 @@ #include <mbgl/renderer/raster_bucket.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/shader/raster_shader.hpp> -#include <mbgl/util/std.hpp> using namespace mbgl; diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 359a9fe9f8..b0b8a6db4f 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -9,6 +9,7 @@ #include <mbgl/text/get_anchors.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/text/glyph_store.hpp> +#include <mbgl/text/font_stack.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/shader/sdf_shader.hpp> @@ -217,7 +218,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, layout.text.justify == TextJustifyType::Left ? 0 : 0.5; - auto* fontStack = glyphStore.getFontStack(layout.text.font); + auto fontStack = glyphStore.getFontStack(layout.text.font); for (const auto& feature : features) { if (!feature.geometry.size()) continue; @@ -241,7 +242,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, // Add the glyphs we need for this label to the glyph atlas. if (shapedText) { - glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, *fontStack, face); + glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, **fontStack, face); } } @@ -322,7 +323,7 @@ void SymbolBucket::placeFeatures() { void SymbolBucket::placeFeatures(bool swapImmediately) { - renderDataInProgress = util::make_unique<SymbolRenderData>(); + renderDataInProgress = std::make_unique<SymbolRenderData>(); // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. @@ -427,7 +428,7 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float if (!buffer.groups.size() || (buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) { // Move to a new group because the old one can't hold the geometry. - buffer.groups.emplace_back(util::make_unique<GroupType>()); + buffer.groups.emplace_back(std::make_unique<GroupType>()); } // We're generating triangle fans, so we always start with the first @@ -488,7 +489,7 @@ void SymbolBucket::addToDebugBuffers() { auto& collisionBox = renderDataInProgress->collisionBox; if (!collisionBox.groups.size()) { // Move to a new group because the old one can't hold the geometry. - collisionBox.groups.emplace_back(util::make_unique<CollisionBoxElementGroup>()); + collisionBox.groups.emplace_back(std::make_unique<CollisionBoxElementGroup>()); } collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom); diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp index a079409aa0..6210c1c32e 100644 --- a/src/mbgl/shader/shader.cpp +++ b/src/mbgl/shader/shader.cpp @@ -51,7 +51,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo if (status == 0) { GLint logLength; MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength)); - std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + const auto log = std::make_unique<GLchar[]>(logLength); if (logLength > 0) { MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get())); Log::Error(Event::Shader, "Program failed to link: %s", log.get()); @@ -76,7 +76,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo if (status == 0) { GLint logLength; MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength)); - std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + const auto log = std::make_unique<GLchar[]>(logLength); if (logLength > 0) { MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get())); Log::Error(Event::Shader, "Program failed to validate: %s", log.get()); @@ -115,7 +115,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { GLint logLength; MBGL_CHECK_ERROR(glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength)); if (logLength > 0) { - std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + const auto log = std::make_unique<GLchar[]>(logLength); MBGL_CHECK_ERROR(glGetShaderInfoLog(*shader, logLength, &logLength, log.get())); Log::Error(Event::Shader, "Shader failed to compile: %s", log.get()); } diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp index e8831f5465..b46696aaa3 100644 --- a/src/mbgl/storage/default_file_source.cpp +++ b/src/mbgl/storage/default_file_source.cpp @@ -5,11 +5,12 @@ #include <mbgl/storage/response.hpp> #include <mbgl/platform/platform.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/util/thread.hpp> -#include <mbgl/platform/log.hpp> +#include <mbgl/util/mapbox.hpp> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" @@ -27,7 +28,7 @@ namespace algo = boost::algorithm; namespace mbgl { DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root) - : thread(util::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) { + : thread(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) { } DefaultFileSource::~DefaultFileSource() { @@ -38,7 +39,27 @@ Request* DefaultFileSource::request(const Resource& resource, uv_loop_t* l, Callback callback) { assert(l); - auto req = new Request(resource, l, std::move(callback)); + + std::string url; + + switch (resource.kind) { + case Resource::Kind::Style: + url = mbgl::util::mapbox::normalizeStyleURL(resource.url, accessToken); + break; + + case Resource::Kind::Source: + url = util::mapbox::normalizeSourceURL(resource.url, accessToken); + break; + + case Resource::Kind::Glyphs: + url = util::mapbox::normalizeGlyphsURL(resource.url, accessToken); + break; + + default: + url = resource.url; + } + + auto req = new Request({ resource.kind, url }, l, std::move(callback)); thread->invoke(&Impl::add, req); return req; } diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp index a6d845ce4a..b653a41e71 100644 --- a/src/mbgl/storage/request.cpp +++ b/src/mbgl/storage/request.cpp @@ -4,7 +4,6 @@ #include <mbgl/storage/response.hpp> #include <mbgl/util/util.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/uv_detail.hpp> #include <cassert> @@ -16,7 +15,7 @@ struct Request::Canceled { std::mutex mutex; bool confirmed = false; }; // 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_) - : async(util::make_unique<uv::async>(loop, [this] { notifyCallback(); })), + : async(std::make_unique<uv::async>(loop, [this] { notifyCallback(); })), callback(callback_), resource(resource_) { } @@ -62,7 +61,7 @@ void Request::notify(const std::shared_ptr<const Response> &response_) { void Request::cancel() { assert(async); assert(!canceled); - canceled = util::make_unique<Canceled>(); + canceled = std::make_unique<Canceled>(); } diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 51d92fdcaf..0acf66eb56 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -5,7 +5,6 @@ #include <mbgl/style/style_parser.hpp> #include <mbgl/style/style_bucket.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/platform/log.hpp> #include <csscolorparser/csscolorparser.hpp> @@ -17,7 +16,7 @@ namespace mbgl { Style::Style() - : mtx(util::make_unique<uv::rwlock>()), + : mtx(std::make_unique<uv::rwlock>()), workers(4) { } diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 313fe3df89..83a113f885 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -3,7 +3,6 @@ #include <mbgl/style/style_layer.hpp> #include <mbgl/map/annotation.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/platform/log.hpp> diff --git a/src/mbgl/text/font_stack.cpp b/src/mbgl/text/font_stack.cpp new file mode 100644 index 0000000000..adabe2eb69 --- /dev/null +++ b/src/mbgl/text/font_stack.cpp @@ -0,0 +1,140 @@ +#include <mbgl/text/font_stack.hpp> +#include <cassert> +#include <mbgl/util/math.hpp> + +namespace mbgl { + +void FontStack::insert(uint32_t id, const SDFGlyph &glyph) { + metrics.emplace(id, glyph.metrics); + bitmaps.emplace(id, glyph.bitmap); + sdfs.emplace(id, glyph); +} + +const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const { + return metrics; +} + +const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const { + return sdfs; +} + +const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth, + const float lineHeight, const float horizontalAlign, + const float verticalAlign, const float justify, + const float spacing, const vec2<float> &translate) const { + Shaping shaping(translate.x * 24, translate.y * 24); + + // the y offset *should* be part of the font metadata + const int32_t yOffset = -17; + + int32_t x = std::round(translate.x * 24); // one em + const int32_t y = std::round(translate.y * 24) + yOffset; // one em + + // Loop through all characters of this label and shape. + for (uint32_t chr : string) { + shaping.positionedGlyphs.emplace_back(chr, x, y); + auto metric = metrics.find(chr); + if (metric != metrics.end()) { + x += metric->second.advance + spacing; + } + } + + if (!shaping.positionedGlyphs.size()) + return shaping; + + lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify); + + return shaping; +} + +void align(Shaping &shaping, const float justify, const float horizontalAlign, + const float verticalAlign, const uint32_t maxLineLength, const float lineHeight, + const uint32_t line) { + const float shiftX = (justify - horizontalAlign) * maxLineLength; + const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight; + + for (auto& glyph : shaping.positionedGlyphs) { + glyph.x += shiftX; + glyph.y += shiftY; + } +} + +void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start, + uint32_t end, float justify) { + PositionedGlyph &glyph = positionedGlyphs[end]; + auto metric = metrics.find(glyph.glyph); + if (metric != metrics.end()) { + const uint32_t lastAdvance = metric->second.advance; + const float lineIndent = float(glyph.x + lastAdvance) * justify; + + for (uint32_t j = start; j <= end; j++) { + positionedGlyphs[j].x -= lineIndent; + } + } +} + +void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth, + const float horizontalAlign, const float verticalAlign, + const float justify) const { + uint32_t lastSafeBreak = 0; + + uint32_t lengthBeforeCurrentLine = 0; + uint32_t lineStartIndex = 0; + uint32_t line = 0; + + uint32_t maxLineLength = 0; + + std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs; + + if (maxWidth) { + for (uint32_t i = 0; i < positionedGlyphs.size(); i++) { + PositionedGlyph &shape = positionedGlyphs[i]; + + shape.x -= lengthBeforeCurrentLine; + shape.y += lineHeight * line; + + if (shape.x > maxWidth && lastSafeBreak > 0) { + + uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x; + maxLineLength = util::max(lineLength, maxLineLength); + + for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { + positionedGlyphs[k].y += lineHeight; + positionedGlyphs[k].x -= lineLength; + } + + if (justify) { + justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify); + } + + lineStartIndex = lastSafeBreak + 1; + lastSafeBreak = 0; + lengthBeforeCurrentLine += lineLength; + line++; + } + + if (shape.glyph == 32) { + lastSafeBreak = i; + } + } + } + + const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back(); + const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph); + assert(lastGlyphMetric != metrics.end()); + const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance; + maxLineLength = std::max(maxLineLength, lastLineLength); + + const uint32_t height = (line + 1) * lineHeight; + + justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify); + align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); + + // Calculate the bounding box + shaping.top += -verticalAlign * height; + shaping.bottom = shaping.top + height; + shaping.left += -horizontalAlign * maxLineLength; + shaping.right = shaping.left + maxLineLength; +} + +} // end namespace mbgl diff --git a/src/mbgl/text/font_stack.hpp b/src/mbgl/text/font_stack.hpp new file mode 100644 index 0000000000..7340cb17a9 --- /dev/null +++ b/src/mbgl/text/font_stack.hpp @@ -0,0 +1,28 @@ +#ifndef MBGL_TEXT_FONT_STACK +#define MBGL_TEXT_FONT_STACK + +#include <mbgl/text/glyph.hpp> +#include <mbgl/util/vec.hpp> + +namespace mbgl { + +class FontStack { +public: + void insert(uint32_t id, const SDFGlyph &glyph); + const std::map<uint32_t, GlyphMetrics> &getMetrics() const; + const std::map<uint32_t, SDFGlyph> &getSDFs() const; + const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight, + float horizontalAlign, float verticalAlign, float justify, + float spacing, const vec2<float> &translate) const; + void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, + float verticalAlign, float justify) const; + +private: + std::map<uint32_t, std::string> bitmaps; + std::map<uint32_t, GlyphMetrics> metrics; + std::map<uint32_t, SDFGlyph> sdfs; +}; + +} // end namespace mbgl + +#endif diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 03016f0fbf..be7c9befd5 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -5,6 +5,7 @@ #include <cstdint> #include <vector> +#include <string> #include <map> namespace mbgl { @@ -68,6 +69,17 @@ class Shaping { operator bool() const { return positionedGlyphs.size(); } }; -} +class SDFGlyph { +public: + uint32_t id = 0; + + // A signed distance field of the glyph with a border of 3 pixels. + std::string bitmap; + + // Glyph metrics + GlyphMetrics metrics; +}; + +} // end namespace mbgl #endif diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp new file mode 100644 index 0000000000..899df39d38 --- /dev/null +++ b/src/mbgl/text/glyph_pbf.cpp @@ -0,0 +1,115 @@ +#include <mbgl/text/glyph_pbf.hpp> +#include <mbgl/text/font_stack.hpp> + +#include <mbgl/map/environment.hpp> + +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> + +#include <mbgl/util/pbf.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/token.hpp> +#include <mbgl/util/url.hpp> + +#include <sstream> + +namespace mbgl { + +GlyphPBF::GlyphPBF(const std::string& glyphURL, + const std::string& fontStack, + GlyphRange glyphRange, + Environment& env_, + const GlyphLoadedCallback& successCallback, + const GlyphLoadingFailedCallback& failureCallback) + : parsed(false), env(env_) { + // Load the glyph set URL + url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { + if (name == "fontstack") return util::percentEncode(fontStack); + if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second); + return ""; + }); + + // The prepare call jumps back to the main thread. + req = env.request({ Resource::Kind::Glyphs, url }, [&, successCallback, failureCallback](const Response &res) { + req = nullptr; + + if (res.status != Response::Successful) { + std::stringstream message; + message << "Failed to load [" << url << "]: " << res.message; + failureCallback(message.str()); + } 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; + successCallback(this); + } + }); +} + +GlyphPBF::~GlyphPBF() { + if (req) { + env.cancelRequest(req); + } +} + +void GlyphPBF::parse(FontStack &stack) { + if (!data.size()) { + // If there is no data, this means we either haven't received any data, or + // we have already parsed the data. + return; + } + + // Parse the glyph PBF + pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size()); + + while (glyphs_pbf.next()) { + if (glyphs_pbf.tag == 1) { // stacks + pbf fontstack_pbf = glyphs_pbf.message(); + while (fontstack_pbf.next()) { + if (fontstack_pbf.tag == 3) { // glyphs + pbf glyph_pbf = fontstack_pbf.message(); + + SDFGlyph glyph; + + while (glyph_pbf.next()) { + if (glyph_pbf.tag == 1) { // id + glyph.id = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 2) { // bitmap + glyph.bitmap = glyph_pbf.string(); + } else if (glyph_pbf.tag == 3) { // width + glyph.metrics.width = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 4) { // height + glyph.metrics.height = glyph_pbf.varint(); + } else if (glyph_pbf.tag == 5) { // left + glyph.metrics.left = glyph_pbf.svarint(); + } else if (glyph_pbf.tag == 6) { // top + glyph.metrics.top = glyph_pbf.svarint(); + } else if (glyph_pbf.tag == 7) { // advance + glyph.metrics.advance = glyph_pbf.varint(); + } else { + glyph_pbf.skip(); + } + } + + stack.insert(glyph.id, glyph); + } else { + fontstack_pbf.skip(); + } + } + } else { + glyphs_pbf.skip(); + } + } + + data.clear(); + + parsed = true; +} + +bool GlyphPBF::isParsed() const { + return parsed; +} + +} diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp new file mode 100644 index 0000000000..bb6fa83ae6 --- /dev/null +++ b/src/mbgl/text/glyph_pbf.hpp @@ -0,0 +1,52 @@ +#ifndef MBGL_TEXT_GLYPH_PBF +#define MBGL_TEXT_GLYPH_PBF + +#include <mbgl/text/glyph.hpp> + +#include <functional> +#include <atomic> +#include <string> + +namespace mbgl { + +class Environment; +class FontStack; +class Request; + +class GlyphPBF { +public: + using GlyphLoadedCallback = std::function<void(GlyphPBF*)>; + using GlyphLoadingFailedCallback = std::function<void(const std::string&)>; + + GlyphPBF(const std::string &glyphURL, + const std::string &fontStack, + GlyphRange glyphRange, + Environment &env, + const GlyphLoadedCallback& successCallback, + const GlyphLoadingFailedCallback& failureCallback); + ~GlyphPBF(); + + void parse(FontStack &stack); + bool isParsed() const; + + std::string getURL() const { + return url; + } + +private: + GlyphPBF(const GlyphPBF &) = delete; + GlyphPBF(GlyphPBF &&) = delete; + GlyphPBF &operator=(const GlyphPBF &) = delete; + GlyphPBF &operator=(GlyphPBF &&) = delete; + + std::string data; + std::string url; + std::atomic<bool> parsed; + + Environment& env; + Request* req = nullptr; +}; + +} // end namespace mbgl + +#endif diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index 37e26b193c..9520b63c06 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -1,264 +1,19 @@ #include <mbgl/text/glyph_store.hpp> +#include <mbgl/text/glyph_pbf.hpp> +#include <mbgl/text/font_stack.hpp> -#include <mbgl/map/environment.hpp> -#include <mbgl/util/std.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/utf.hpp> -#include <mbgl/util/pbf.hpp> -#include <mbgl/util/url.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/token.hpp> -#include <mbgl/util/math.hpp> +#include <mbgl/util/exception.hpp> #include <mbgl/util/uv_detail.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/platform/log.hpp> -#include <mbgl/platform/platform.hpp> -#include <mbgl/util/uv_detail.hpp> -#include <algorithm> namespace mbgl { - -void FontStack::insert(uint32_t id, const SDFGlyph &glyph) { - std::lock_guard<std::mutex> lock(mtx); - metrics.emplace(id, glyph.metrics); - bitmaps.emplace(id, glyph.bitmap); - sdfs.emplace(id, glyph); -} - -const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const { - std::lock_guard<std::mutex> lock(mtx); - return metrics; -} - -const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const { - std::lock_guard<std::mutex> lock(mtx); - return sdfs; -} - -const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth, - const float lineHeight, const float horizontalAlign, - const float verticalAlign, const float justify, - const float spacing, const vec2<float> &translate) const { - std::lock_guard<std::mutex> lock(mtx); - - Shaping shaping(translate.x * 24, translate.y * 24); - - // the y offset *should* be part of the font metadata - const int32_t yOffset = -17; - - int32_t x = std::round(translate.x * 24); // one em - const int32_t y = std::round(translate.y * 24) + yOffset; // one em - - // Loop through all characters of this label and shape. - for (uint32_t chr : string) { - shaping.positionedGlyphs.emplace_back(chr, x, y); - auto metric = metrics.find(chr); - if (metric != metrics.end()) { - x += metric->second.advance + spacing; - } - } - - if (!shaping.positionedGlyphs.size()) - return shaping; - - lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify); - - return shaping; -} - -void align(Shaping &shaping, const float justify, const float horizontalAlign, - const float verticalAlign, const uint32_t maxLineLength, const float lineHeight, - const uint32_t line) { - const float shiftX = (justify - horizontalAlign) * maxLineLength; - const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight; - - for (auto& glyph : shaping.positionedGlyphs) { - glyph.x += shiftX; - glyph.y += shiftY; - } -} - -void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start, - uint32_t end, float justify) { - PositionedGlyph &glyph = positionedGlyphs[end]; - auto metric = metrics.find(glyph.glyph); - if (metric != metrics.end()) { - const uint32_t lastAdvance = metric->second.advance; - const float lineIndent = float(glyph.x + lastAdvance) * justify; - - for (uint32_t j = start; j <= end; j++) { - positionedGlyphs[j].x -= lineIndent; - } - } -} - -void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth, - const float horizontalAlign, const float verticalAlign, - const float justify) const { - uint32_t lastSafeBreak = 0; - - uint32_t lengthBeforeCurrentLine = 0; - uint32_t lineStartIndex = 0; - uint32_t line = 0; - - uint32_t maxLineLength = 0; - - std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs; - - if (maxWidth) { - for (uint32_t i = 0; i < positionedGlyphs.size(); i++) { - PositionedGlyph &shape = positionedGlyphs[i]; - - shape.x -= lengthBeforeCurrentLine; - shape.y += lineHeight * line; - - if (shape.x > maxWidth && lastSafeBreak > 0) { - - uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x; - maxLineLength = util::max(lineLength, maxLineLength); - - for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { - positionedGlyphs[k].y += lineHeight; - positionedGlyphs[k].x -= lineLength; - } - - if (justify) { - justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify); - } - - lineStartIndex = lastSafeBreak + 1; - lastSafeBreak = 0; - lengthBeforeCurrentLine += lineLength; - line++; - } - - if (shape.glyph == 32) { - lastSafeBreak = i; - } - } - } - - const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back(); - const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph); - assert(lastGlyphMetric != metrics.end()); - const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance; - maxLineLength = std::max(maxLineLength, lastLineLength); - - const uint32_t height = (line + 1) * lineHeight; - - justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify); - align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); - - // Calculate the bounding box - shaping.top += -verticalAlign * height; - shaping.bottom = shaping.top + height; - shaping.left += -horizontalAlign * maxLineLength; - shaping.right = shaping.left + maxLineLength; -} - -GlyphPBF::GlyphPBF(const std::string& glyphURL, - const std::string& fontStack, - GlyphRange glyphRange, - Environment& env_, - const GlyphLoadedCallback& callback) - : parsed(false), env(env_) { - // Load the glyph set URL - std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { - if (name == "fontstack") return util::percentEncode(fontStack); - if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second); - return ""; - }); - - // The prepare call jumps back to the main thread. - req = env.request({ Resource::Kind::Glyphs, url }, [&, url, callback](const Response &res) { - req = nullptr; - - if (res.status != Response::Successful) { - // Something went wrong with loading the glyph pbf. - const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + url + " message: " + res.message; - Log::Error(Event::HttpRequest, 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; - parsed = true; - callback(this); - } - }); -} - -GlyphPBF::~GlyphPBF() { - if (req) { - env.cancelRequest(req); - } -} - -void GlyphPBF::parse(FontStack &stack) { - std::lock_guard<std::mutex> lock(mtx); - - if (!data.size()) { - // If there is no data, this means we either haven't received any data, or - // we have already parsed the data. - return; - } - - // Parse the glyph PBF - pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size()); - - while (glyphs_pbf.next()) { - if (glyphs_pbf.tag == 1) { // stacks - pbf fontstack_pbf = glyphs_pbf.message(); - while (fontstack_pbf.next()) { - if (fontstack_pbf.tag == 3) { // glyphs - pbf glyph_pbf = fontstack_pbf.message(); - - SDFGlyph glyph; - - while (glyph_pbf.next()) { - if (glyph_pbf.tag == 1) { // id - glyph.id = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 2) { // bitmap - glyph.bitmap = glyph_pbf.string(); - } else if (glyph_pbf.tag == 3) { // width - glyph.metrics.width = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 4) { // height - glyph.metrics.height = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 5) { // left - glyph.metrics.left = glyph_pbf.svarint(); - } else if (glyph_pbf.tag == 6) { // top - glyph.metrics.top = glyph_pbf.svarint(); - } else if (glyph_pbf.tag == 7) { // advance - glyph.metrics.advance = glyph_pbf.varint(); - } else { - glyph_pbf.skip(); - } - } - - stack.insert(glyph.id, glyph); - } else { - fontstack_pbf.skip(); - } - } - } else { - glyphs_pbf.skip(); - } - } - - data.clear(); -} - -bool GlyphPBF::isParsed() const { - return parsed; -} - GlyphStore::GlyphStore(uv_loop_t* loop, Environment& env_) : env(env_), - asyncEmitGlyphRangeLoaded(util::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })), + asyncEmitGlyphRangeLoaded(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })), + asyncEmitGlyphRangeLoadedingFailed(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoadingFailed(); })), observer(nullptr) { asyncEmitGlyphRangeLoaded->unref(); + asyncEmitGlyphRangeLoadedingFailed->unref(); } GlyphStore::~GlyphStore() { @@ -269,7 +24,7 @@ void GlyphStore::setURL(const std::string &url) { glyphURL = url; } -bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, +bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStackName, const std::set<GlyphRange>& glyphRanges) { bool requestIsNeeded = false; @@ -277,18 +32,32 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, return requestIsNeeded; } - auto callback = [this, fontStack](GlyphPBF* glyph) { - glyph->parse(*createFontStack(fontStack)); - asyncEmitGlyphRangeLoaded->send(); + auto successCallback = [this, fontStackName](GlyphPBF* glyph) { + auto fontStack = createFontStack(fontStackName); + try { + glyph->parse(**fontStack); + asyncEmitGlyphRangeLoaded->send(); + } catch (const std::exception&) { + std::lock_guard<std::mutex> lock(errorMessageMutex); + errorMessage = "Failed to parse [" + glyph->getURL() + "]"; + asyncEmitGlyphRangeLoadedingFailed->send(); + } + }; + + auto failureCallback = [this](const std::string& message) { + std::lock_guard<std::mutex> lock(errorMessageMutex); + errorMessage = message; + asyncEmitGlyphRangeLoadedingFailed->send(); }; std::lock_guard<std::mutex> lock(rangesMutex); - auto& rangeSets = ranges[fontStack]; + auto& rangeSets = ranges[fontStackName]; for (const auto& range : glyphRanges) { const auto& rangeSets_it = rangeSets.find(range); if (rangeSets_it == rangeSets.end()) { - auto glyph = util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env, callback); + auto glyph = std::make_unique<GlyphPBF>(glyphURL, fontStackName, range, env, + successCallback, failureCallback); rangeSets.emplace(range, std::move(glyph)); requestIsNeeded = true; continue; @@ -302,26 +71,26 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, return requestIsNeeded; } -FontStack* GlyphStore::createFontStack(const std::string &fontStack) { - std::lock_guard<std::mutex> lock(stacksMutex); +util::exclusive<FontStack> GlyphStore::createFontStack(const std::string &fontStack) { + auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex); auto stack_it = stacks.find(fontStack); if (stack_it == stacks.end()) { - stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first; + stack_it = stacks.emplace(fontStack, std::make_unique<FontStack>()).first; } - return stack_it->second.get(); + return { stack_it->second.get(), std::move(lock) }; } -FontStack* GlyphStore::getFontStack(const std::string &fontStack) { - std::lock_guard<std::mutex> lock(stacksMutex); +util::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) { + auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex); const auto& stack_it = stacks.find(fontStack); if (stack_it == stacks.end()) { - return nullptr; + return { nullptr, nullptr }; } - return stack_it->second.get(); + return { stack_it->second.get(), std::move(lock) }; } void GlyphStore::setObserver(Observer* observer_) { @@ -334,4 +103,14 @@ void GlyphStore::emitGlyphRangeLoaded() { } } +void GlyphStore::emitGlyphRangeLoadingFailed() { + if (!observer) { + return; + } + + std::lock_guard<std::mutex> lock(errorMessageMutex); + auto error = std::make_exception_ptr(util::GlyphRangeLoadingException(errorMessage)); + observer->onGlyphRangeLoadingFailed(error); +} + } diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp index efa848dd08..746bcd1338 100644 --- a/src/mbgl/text/glyph_store.hpp +++ b/src/mbgl/text/glyph_store.hpp @@ -2,90 +2,27 @@ #define MBGL_TEXT_GLYPH_STORE #include <mbgl/text/glyph.hpp> -#include <mbgl/util/vec.hpp> -#include <mbgl/util/ptr.hpp> -#include <cstdint> -#include <vector> -#include <future> -#include <map> +#include <mbgl/util/exclusive.hpp> + #include <set> #include <string> #include <unordered_map> +#include <exception> typedef struct uv_loop_s uv_loop_t; namespace uv { - class async; - } namespace mbgl { -class FileSource; class Environment; -class Request; - -class SDFGlyph { -public: - uint32_t id = 0; - - // A signed distance field of the glyph with a border of 3 pixels. - std::string bitmap; - - // Glyph metrics - GlyphMetrics metrics; -}; - -class FontStack { -public: - void insert(uint32_t id, const SDFGlyph &glyph); - const std::map<uint32_t, GlyphMetrics> &getMetrics() const; - const std::map<uint32_t, SDFGlyph> &getSDFs() const; - const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight, - float horizontalAlign, float verticalAlign, float justify, - float spacing, const vec2<float> &translate) const; - void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, - float verticalAlign, float justify) const; - -private: - std::map<uint32_t, std::string> bitmaps; - std::map<uint32_t, GlyphMetrics> metrics; - std::map<uint32_t, SDFGlyph> sdfs; - mutable std::mutex mtx; -}; - -class GlyphPBF { -public: - using GlyphLoadedCallback = std::function<void(GlyphPBF*)>; - - GlyphPBF(const std::string &glyphURL, - const std::string &fontStack, - GlyphRange glyphRange, - Environment &env, - const GlyphLoadedCallback& callback); - ~GlyphPBF(); - - void parse(FontStack &stack); - bool isParsed() const; - -private: - GlyphPBF(const GlyphPBF &) = delete; - GlyphPBF(GlyphPBF &&) = delete; - GlyphPBF &operator=(const GlyphPBF &) = delete; - GlyphPBF &operator=(GlyphPBF &&) = delete; - - std::string data; - std::atomic_bool parsed; - - Environment& env; - Request* req = nullptr; +class FontStack; +class GlyphPBF; - mutable std::mutex mtx; -}; - -// Manages Glyphrange PBF loading. +// Manages GlyphRange PBF loading. class GlyphStore { public: class Observer { @@ -93,6 +30,7 @@ public: virtual ~Observer() = default; virtual void onGlyphRangeLoaded() = 0; + virtual void onGlyphRangeLoadingFailed(std::exception_ptr error) = 0; }; GlyphStore(uv_loop_t* loop, Environment &); @@ -104,7 +42,7 @@ public: // GlyphRanges are already available, and thus, no request is performed. bool requestGlyphRangesIfNeeded(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges); - FontStack* getFontStack(const std::string &fontStack); + util::exclusive<FontStack> getFontStack(const std::string &fontStack); void setURL(const std::string &url); @@ -112,8 +50,9 @@ public: private: void emitGlyphRangeLoaded(); + void emitGlyphRangeLoadingFailed(); - FontStack* createFontStack(const std::string &fontStack); + util::exclusive<FontStack> createFontStack(const std::string &fontStack); std::string glyphURL; Environment &env; @@ -124,7 +63,11 @@ private: std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks; std::mutex stacksMutex; + std::string errorMessage; + std::mutex errorMessageMutex; + std::unique_ptr<uv::async> asyncEmitGlyphRangeLoaded; + std::unique_ptr<uv::async> asyncEmitGlyphRangeLoadedingFailed; Observer* observer; }; diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp index f2171a6165..b6b5df27f6 100644 --- a/src/mbgl/util/raster.cpp +++ b/src/mbgl/util/raster.cpp @@ -4,7 +4,6 @@ #include <mbgl/util/raster.hpp> #include <mbgl/util/uv_detail.hpp> -#include <mbgl/util/std.hpp> #include <cassert> #include <cstring> @@ -27,7 +26,7 @@ bool Raster::isLoaded() const { } bool Raster::load(const std::string &data) { - img = util::make_unique<util::Image>(data); + img = std::make_unique<util::Image>(data); width = img->getWidth(); height = img->getHeight(); diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp index ed2b364cef..1c92847b69 100644 --- a/src/mbgl/util/run_loop.hpp +++ b/src/mbgl/util/run_loop.hpp @@ -2,10 +2,10 @@ #define MBGL_UTIL_RUN_LOOP #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/std.hpp> #include <mbgl/util/uv_detail.hpp> #include <functional> +#include <utility> #include <queue> #include <mutex> @@ -19,37 +19,38 @@ public: void stop(); - // Invoke fn() in the runloop thread. - template <class Fn> - void invoke(Fn&& fn) { - auto invokable = util::make_unique<Invoker<Fn>>(std::move(fn)); + // Invoke fn(args...) on this RunLoop. + template <class Fn, class... Args> + void invoke(Fn&& fn, Args&&... args) { + auto tuple = std::make_tuple(std::move(args)...); + auto invokable = std::make_unique<Invoker<Fn, decltype(tuple), Args...>>(std::move(fn), std::move(tuple)); withMutex([&] { queue.push(std::move(invokable)); }); async.send(); } - // Invoke fn() in the runloop thread, then invoke callback(result) in the current thread. - template <class Fn, class R> - void invokeWithResult(Fn&& fn, std::function<void (R)>&& callback) { - RunLoop* outer = current.get(); - assert(outer); - - invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable { - outer->invoke([callback = std::move(callback), result = std::move(fn())] () mutable { - callback(std::move(result)); - }); - }); + // Return a function that invokes the given function on this RunLoop. + template <class... Args> + auto bind(std::function<void (Args...)> fn) { + return [this, fn = std::move(fn)] (Args&&... args) { + invoke(std::move(fn), std::move(args)...); + }; } - // Invoke fn() in the runloop thread, then invoke callback() in the current thread. - template <class Fn> - void invokeWithResult(Fn&& fn, std::function<void ()>&& callback) { - RunLoop* outer = current.get(); - assert(outer); + // Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop. + template <class R, class Fn, class... Args> + void invokeWithResult(Fn&& fn, std::function<void (R)> callback, Args&&... args) { + invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable { + callback(fn(std::forward<Args>(a)...)); + }, std::forward<Args>(args)...); + } - invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable { - fn(); - outer->invoke(std::move(callback)); - }); + // Invoke fn(args...) on this RunLoop, then invoke callback() on the current RunLoop. + template <class Fn, class... Args> + void invokeWithResult(Fn&& fn, std::function<void ()> callback, Args&&... args) { + invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable { + fn(std::forward<Args>(a)...); + callback(); + }, std::forward<Args>(args)...); } uv_loop_t* get() { return async.get()->loop; } @@ -65,11 +66,24 @@ private: virtual ~Message() = default; }; - template <class F> + template <class F, class P, class... Args> struct Invoker : Message { - Invoker(F&& f) : func(std::move(f)) {} - void operator()() override { func(); } + Invoker(F&& f, P&& p) + : func(std::move(f)), + params(std::move(p)) { + } + + void operator()() override { + invoke(std::index_sequence_for<Args...>{}); + } + + template <std::size_t... I> + void invoke(std::index_sequence<I...>) { + func(std::forward<Args>(std::get<I>(params))...); + } + F func; + P params; }; using Queue = std::queue<std::unique_ptr<Message>>; diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index a2ad958645..f3a9baa6f3 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -4,26 +4,12 @@ #include <future> #include <thread> #include <atomic> +#include <utility> #include <functional> #include <mbgl/util/run_loop.hpp> #include <mbgl/platform/platform.hpp> -namespace { - -template <::std::size_t...> -struct index_sequence {}; - -template <::std::size_t N, ::std::size_t... I> -struct integer_sequence : integer_sequence<N - 1, N - 1, I...> {}; - -template <::std::size_t... I> -struct integer_sequence<0, I...> { - using type = index_sequence<I...>; -}; - -} - namespace mbgl { namespace util { @@ -50,19 +36,19 @@ public: // Invoke object->fn(args...) in the runloop thread. template <typename Fn, class... Args> void invoke(Fn fn, Args&&... args) { - loop->invoke(std::bind(fn, object, args...)); + loop->invoke(bind<Fn, Args...>(fn), std::forward<Args>(args)...); } // Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread. - template <typename Fn, class R, class... Args> - void invokeWithResult(Fn fn, std::function<void (R)>&& callback, Args&&... args) { - loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback)); + template <class R, typename Fn, class... Args> + void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) { + loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...); } // Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread. template <typename Fn, class... Args> - void invokeWithResult(Fn fn, std::function<void ()>&& callback, Args&&... args) { - loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback)); + void invokeWithResult(Fn fn, std::function<void ()> callback, Args&&... args) { + loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...); } // Invoke object->fn(args...) in the runloop thread, and wait for the result. @@ -89,8 +75,13 @@ private: Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; + template <typename Fn, class... Args> + auto bind(Fn fn) { + return [fn, this] (Args&&... a) { return (object->*fn)(std::forward<Args>(a)...); }; + } + template <typename P, std::size_t... I> - void run(P&& params, index_sequence<I...>); + void run(P&& params, std::index_sequence<I...>); std::promise<void> running; std::promise<void> joinable; @@ -119,8 +110,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&. platform::makeThreadLowPriority(); } - constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type(); - run(std::move(params), seq); + run(std::move(params), std::index_sequence_for<Args...>{}); }); running.get_future().get(); @@ -128,7 +118,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&. template <class Object> template <typename P, std::size_t... I> -void Thread<Object>::run(P&& params, index_sequence<I...>) { +void Thread<Object>::run(P&& params, std::index_sequence<I...>) { uv::loop l; { diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp index 984c55c95e..3022d30277 100644 --- a/src/mbgl/util/worker.cpp +++ b/src/mbgl/util/worker.cpp @@ -19,7 +19,7 @@ public: Worker::Worker(std::size_t count) { for (std::size_t i = 0; i < count; i++) { - threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low)); + threads.emplace_back(std::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low)); } } @@ -27,7 +27,7 @@ Worker::~Worker() = default; std::unique_ptr<WorkRequest> Worker::send(Fn work, Fn after) { auto task = std::make_shared<WorkTask>(work, after); - auto request = util::make_unique<WorkRequest>(task); + auto request = std::make_unique<WorkRequest>(task); threads[current]->invokeWithResult(&Worker::Impl::doWork, [task] { task->runAfter(); |