Files
MapLibreParticleLab/src/mbgl/map/map_context.cpp
Bruno de Oliveira Abinader 38f3bed63e Export default transition delay in Map public API
Added a setter/getter for default transition delay, in the same fashion
as the default transition duration.

Spin-off from #1888. Fixes #302.
2015-07-21 18:02:09 +03:00

411 lines
12 KiB
C++

#include <mbgl/map/map_context.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/map/annotation.hpp>
#include <mbgl/annotation/sprite_store.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/util/gl_object_store.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/worker.hpp>
#include <mbgl/util/texture_pool.hpp>
#include <mbgl/util/exception.hpp>
#include <algorithm>
namespace mbgl {
MapContext::MapContext(View& view_, FileSource& fileSource, MapData& data_)
: view(view_),
data(data_),
updated(static_cast<UpdateType>(Update::Nothing)),
asyncUpdate(std::make_unique<uv::async>(util::RunLoop::getLoop(), [this] { update(); })),
texturePool(std::make_unique<TexturePool>()) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
util::ThreadContext::setFileSource(&fileSource);
util::ThreadContext::setGLObjectStore(&glObjectStore);
asyncUpdate->unref();
view.activate();
}
MapContext::~MapContext() {
// Make sure we call cleanup() before deleting this object.
assert(!style);
}
void MapContext::cleanup() {
view.notify();
if (styleRequest) {
FileSource* fs = util::ThreadContext::getFileSource();
fs->cancel(styleRequest);
styleRequest = nullptr;
}
// Explicit resets currently necessary because these abandon resources that need to be
// cleaned up by glObjectStore.performCleanup();
style.reset();
painter.reset();
texturePool.reset();
glObjectStore.performCleanup();
view.deactivate();
}
void MapContext::pause() {
MBGL_CHECK_ERROR(glFinish());
view.deactivate();
std::unique_lock<std::mutex> lockPause(data.mutexPause);
data.condPaused.notify_all();
data.condResume.wait(lockPause);
view.activate();
}
void MapContext::triggerUpdate(const TransformState& state, const Update u) {
transformState = state;
updated |= static_cast<UpdateType>(u);
asyncUpdate->send();
}
void MapContext::setStyleURL(const std::string& url) {
FileSource* fs = util::ThreadContext::getFileSource();
if (styleRequest) {
fs->cancel(styleRequest);
}
styleURL = url;
styleJSON.clear();
style = std::make_unique<Style>(data, asyncUpdate->get()->loop);
const size_t pos = styleURL.rfind('/');
std::string base = "";
if (pos != std::string::npos) {
base = styleURL.substr(0, pos + 1);
}
styleRequest = fs->request({ Resource::Kind::Style, styleURL }, util::RunLoop::getLoop(), [this, base](const Response &res) {
styleRequest = nullptr;
if (res.status == Response::Successful) {
loadStyleJSON(res.data, base);
} else {
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
}
});
}
void MapContext::setStyleJSON(const std::string& json, const std::string& base) {
styleURL.clear();
styleJSON = json;
style = std::make_unique<Style>(data, asyncUpdate->get()->loop);
loadStyleJSON(json, base);
}
void MapContext::loadStyleJSON(const std::string& json, const std::string& base) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
style->setJSON(json, base);
style->setObserver(this);
// force style cascade, causing all pending transitions to complete.
style->cascade();
updated |= static_cast<UpdateType>(Update::DefaultTransition);
updated |= static_cast<UpdateType>(Update::Classes);
updated |= static_cast<UpdateType>(Update::Zoom);
asyncUpdate->send();
auto staleTiles = data.getAnnotationManager()->resetStaleTiles();
if (staleTiles.size()) {
updateAnnotationTiles(staleTiles);
}
}
void MapContext::updateAnnotationTiles(const std::unordered_set<TileID, TileID::Hash>& ids) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
util::exclusive<AnnotationManager> annotationManager = data.getAnnotationManager();
annotationManager->markStaleTiles(ids);
if (!style) {
return;
}
// grab existing, single shape annotations source
const auto& shapeID = AnnotationManager::ShapeLayerID;
Source* shapeAnnotationSource = style->getSource(shapeID);
// Style not parsed yet
if (!shapeAnnotationSource) {
return;
}
shapeAnnotationSource->enabled = true;
// create (if necessary) layers and buckets for each shape
for (const auto &shapeAnnotationID : annotationManager->getOrderedShapeAnnotations()) {
const std::string shapeLayerID = shapeID + "." + std::to_string(shapeAnnotationID);
const auto layer_it = std::find_if(style->layers.begin(), style->layers.end(),
[&shapeLayerID](util::ptr<StyleLayer> layer) {
return (layer->id == shapeLayerID);
});
if (layer_it == style->layers.end()) {
// query shape styling
auto& shapeStyle = annotationManager->getAnnotationStyleProperties(shapeAnnotationID);
// apply shape paint properties
ClassProperties paintProperties;
if (shapeStyle.is<LineProperties>()) {
// opacity
PropertyValue lineOpacity = ConstantFunction<float>(shapeStyle.get<LineProperties>().opacity);
paintProperties.set(PropertyKey::LineOpacity, lineOpacity);
// line width
PropertyValue lineWidth = ConstantFunction<float>(shapeStyle.get<LineProperties>().width);
paintProperties.set(PropertyKey::LineWidth, lineWidth);
// stroke color
PropertyValue strokeColor = ConstantFunction<Color>(shapeStyle.get<LineProperties>().color);
paintProperties.set(PropertyKey::LineColor, strokeColor);
} else if (shapeStyle.is<FillProperties>()) {
// opacity
PropertyValue fillOpacity = ConstantFunction<float>(shapeStyle.get<FillProperties>().opacity);
paintProperties.set(PropertyKey::FillOpacity, fillOpacity);
// fill color
PropertyValue fillColor = ConstantFunction<Color>(shapeStyle.get<FillProperties>().fill_color);
paintProperties.set(PropertyKey::FillColor, fillColor);
// stroke color
PropertyValue strokeColor = ConstantFunction<Color>(shapeStyle.get<FillProperties>().stroke_color);
paintProperties.set(PropertyKey::FillOutlineColor, strokeColor);
}
std::map<ClassID, ClassProperties> shapePaints;
shapePaints.emplace(ClassID::Default, std::move(paintProperties));
// create shape layer
util::ptr<StyleLayer> shapeLayer = std::make_shared<StyleLayer>(shapeLayerID, std::move(shapePaints));
shapeLayer->type = (shapeStyle.is<LineProperties>() ? StyleLayerType::Line : StyleLayerType::Fill);
// add to end of other shape layers just before (last) point layer
style->layers.emplace((style->layers.end() - 1), shapeLayer);
// create shape bucket & connect to source
util::ptr<StyleBucket> shapeBucket = std::make_shared<StyleBucket>(shapeLayer->type);
shapeBucket->name = shapeLayer->id;
shapeBucket->source = shapeID;
shapeBucket->source_layer = shapeLayer->id;
// apply line layout properties to bucket
if (shapeStyle.is<LineProperties>()) {
shapeBucket->layout.set(PropertyKey::LineJoin, ConstantFunction<JoinType>(JoinType::Round));
}
// connect layer to bucket
shapeLayer->bucket = shapeBucket;
}
}
// invalidate annotations layer tiles
for (const auto &source : style->sources) {
if (source->info.type == SourceType::Annotations) {
source->invalidateTiles(ids);
}
}
updated |= static_cast<UpdateType>(Update::Classes);
asyncUpdate->send();
annotationManager->resetStaleTiles();
}
void MapContext::update() {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (!style) {
updated = static_cast<UpdateType>(Update::Nothing);
return;
}
data.setAnimationTime(Clock::now());
if (updated & static_cast<UpdateType>(Update::Classes)) {
style->cascade();
}
if (updated & static_cast<UpdateType>(Update::Classes) ||
updated & static_cast<UpdateType>(Update::Zoom)) {
style->recalculate(transformState.getNormalizedZoom());
}
style->update(transformState, *texturePool);
if (data.mode == MapMode::Continuous) {
view.invalidate();
} else if (callback && style->isLoaded()) {
renderSync(transformState, frameData);
}
updated = static_cast<UpdateType>(Update::Nothing);
}
void MapContext::renderStill(const TransformState& state, const FrameData& frame, StillImageCallback fn) {
if (!fn) {
Log::Error(Event::General, "StillImageCallback not set");
return;
}
if (data.mode != MapMode::Still) {
fn(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), nullptr);
return;
}
if (callback) {
fn(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), nullptr);
return;
}
if (!style) {
fn(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), nullptr);
return;
}
if (style->getLastError()) {
fn(style->getLastError(), nullptr);
return;
}
callback = fn;
transformState = state;
frameData = frame;
updated |= static_cast<UpdateType>(Update::RenderStill);
asyncUpdate->send();
}
MapContext::RenderResult MapContext::renderSync(const TransformState& state, const FrameData& frame) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
// Style was not loaded yet.
if (!style) {
return { false, false };
}
transformState = state;
// Cleanup OpenGL objects that we abandoned since the last render call.
glObjectStore.performCleanup();
if (!painter) {
painter = std::make_unique<Painter>(data);
painter->setup();
}
painter->setDebug(data.getDebug());
painter->render(*style, transformState, frame, data.getAnimationTime());
if (data.mode == MapMode::Still) {
callback(nullptr, view.readStillImage());
callback = nullptr;
}
view.swap();
return RenderResult {
style->isLoaded(),
style->hasTransitions() || painter->needsAnimation()
};
}
bool MapContext::isLoaded() const {
return style->isLoaded();
}
double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
auto sprite = style->spriteStore->getSprite(symbol);
if (sprite) {
return -sprite->height / 2;
} else {
return 0;
}
}
void MapContext::setSourceTileCacheSize(size_t size) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (size != sourceCacheSize) {
sourceCacheSize = size;
if (!style) return;
for (const auto &source : style->sources) {
source->setCacheSize(sourceCacheSize);
}
view.invalidate();
}
}
void MapContext::onLowMemory() {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (!style) return;
for (const auto &source : style->sources) {
source->onLowMemory();
}
view.invalidate();
}
void MapContext::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
if (!style) {
Log::Info(Event::Sprite, "Ignoring sprite without stylesheet");
return;
}
style->spriteStore->setSprite(name, sprite);
style->spriteAtlas->updateDirty();
}
void MapContext::onTileDataChanged() {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
asyncUpdate->send();
}
void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (data.mode == MapMode::Still && callback) {
callback(error, nullptr);
callback = nullptr;
}
}
}