diff options
author | Sudarsana Babu Nagineni <sudarsana.babu@mapbox.com> | 2019-05-27 17:47:12 +0300 |
---|---|---|
committer | Sudarsana Babu Nagineni <sudarsana.babu@mapbox.com> | 2019-06-10 11:25:55 +0300 |
commit | 93506919477f8efad5ad0af8f7d6d8a9c03e3660 (patch) | |
tree | 4667aaf4e1101f85dec2b7278f7759b2ffacd5fd | |
parent | fc802daa629d11174cd868820f584399501a33c7 (diff) | |
download | qtlocation-mapboxgl-upstream/nagineni-map-refactor.tar.gz |
[core] Refactor Map interfaceupstream/nagineni-map-refactor
27 files changed, 1738 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ca33c36be2..3bc5ebf1d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,10 @@ if(COMMAND mbgl_platform_glfw) include(cmake/glfw.cmake) endif() +if(COMMAND mapbox_glfw_app) + include(cmake/mapbox-glfw-app.cmake) +endif() + if(COMMAND mbgl_platform_render) include(cmake/render.cmake) endif() @@ -370,6 +370,14 @@ glfw-app: $(LINUX_BUILD) run-glfw-app: glfw-app cd $(LINUX_OUTPUT_PATH) && ./mbgl-glfw +.PHONY: mapbox-glfw-app +mapbox-glfw-app: $(LINUX_BUILD) + $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mapbox-glfw-app + +.PHONY: run-mapbox-glfw-app +run-mapbox-glfw-app: mapbox-glfw-app + cd $(LINUX_OUTPUT_PATH) && ./mapbox-glfw-app + .PHONY: node node: $(LINUX_BUILD) $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.active diff --git a/cmake/mapbox-glfw-app.cmake b/cmake/mapbox-glfw-app.cmake new file mode 100644 index 0000000000..33cdf1f4be --- /dev/null +++ b/cmake/mapbox-glfw-app.cmake @@ -0,0 +1,21 @@ +add_executable(mapbox-glfw-app + ${CMAKE_SOURCE_DIR}/examples/glfw/main.cpp +) + +target_sources(mapbox-glfw-app + PRIVATE ${CMAKE_SOURCE_DIR}/examples/glfw/window.cpp +) + +target_include_directories(mapbox-glfw-app + PRIVATE platform/default/include + PRIVATE include/mapbox/map +) + +target_link_libraries(mapbox-glfw-app + PRIVATE mbgl-core + PRIVATE glfw + PRIVATE cheap-ruler-cpp + PRIVATE args +) + +mapbox_glfw_app() diff --git a/examples/glfw/main.cpp b/examples/glfw/main.cpp new file mode 100644 index 0000000000..dbb27a7283 --- /dev/null +++ b/examples/glfw/main.cpp @@ -0,0 +1,33 @@ +#include "window.hpp" + +#include <GLFW/glfw3.h> + +#include <iostream> + +void glfwError(int error, const char* description) { + std::cerr << "GLFW error (" << error << "): " << description << std::endl; + exit(1); +} + +int main() { + glfwSetErrorCallback(glfwError); + glfwInit(); + +#ifdef MBGL_USE_GLES2 + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); +#endif + + glfwWindowHint(GLFW_RED_BITS, 8); + glfwWindowHint(GLFW_GREEN_BITS, 8); + glfwWindowHint(GLFW_BLUE_BITS, 8); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_STENCIL_BITS, 8); + glfwWindowHint(GLFW_DEPTH_BITS, 16); + Window window; + window.run(); + + return 0; +} diff --git a/examples/glfw/window.cpp b/examples/glfw/window.cpp new file mode 100644 index 0000000000..38ec6bbdce --- /dev/null +++ b/examples/glfw/window.cpp @@ -0,0 +1,208 @@ +#include "window.hpp" + +#include <mbgl/map/map_options.hpp> +#include <mbgl/storage/resource_options.hpp> +#include <mbgl/map/camera.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/optional.hpp> + +#include <GLFW/glfw3.h> + +#include <cassert> +#include <iostream> + +std::string getEnvVar(const std::string& var) { + const char* value = getenv(var.c_str()); + if (value != nullptr) { + return value; + } + + return ""; +} + +Window::Window() : window_(glfwCreateWindow(mapSize_.width, mapSize_.height, "Mapbox GLFW lite", nullptr, nullptr)) { + glfwMakeContextCurrent(window_); + glfwSwapInterval(1); + glfwSetWindowUserPointer(window_, this); + glfwSetKeyCallback(window_, onKey); + glfwSetCursorPosCallback(window_, onMouseMove); + glfwSetMouseButtonCallback(window_, onMouseClick); + glfwSetScrollCallback(window_, onScroll); + glfwSetWindowSizeCallback(window_, onWindowResize); + + // Get the FB size to determine the pixel ratio + int fbWidth = 0; + int fbHeight = 0; + glfwGetFramebufferSize(window_, &fbWidth, &fbHeight); + float aspectRatio_ = float(fbWidth) / float(mapSize_.width); + assert(aspectRatio_ == float(fbHeight) / float(mapSize_.height)); + + std::string accessToken; + accessToken.append(getEnvVar("MAPBOX_ACCESS_TOKEN")); + if (accessToken.empty()) { + std::cerr << "No MAPBOX_ACCESS_TOKEN set" << std::endl; + } + + mbgl::MapOptions mapOptions; + mapOptions.withSize(mapSize_).withPixelRatio(aspectRatio_); + + mbgl::ResourceOptions resourceOptions; + resourceOptions.withAccessToken(accessToken) + .withCachePath("/tmp/mbgl-cache.db"); + + map_ = std::make_unique<mapbox::map::Map>(std::make_shared<MapClient>(*this), + std::make_shared<Observer>(), std::move(mapOptions), std::move(resourceOptions)); + map_->setStyleURL("mapbox://styles/mapbox/streets-v11"); + + map_->jumpTo(mbgl::CameraOptions() + .withCenter(mbgl::LatLng {60.170448, 24.942046}) + .withZoom(10)); +} + +Window::~Window() { + map_.reset(); + glfwDestroyWindow(window_); + glfwTerminate(); +} + +void Window::render() { + if (needsRepaint_) { + needsRepaint_ = false; + map_->render(); + glfwSwapBuffers(window_); + } +} + +bool Window::shouldClose() const { + return glfwWindowShouldClose(window_) == 1; +} + +void Window::scheduleRepaint() { + needsRepaint_ = true; + glfwPostEmptyEvent(); +} + +void Window::onKey(GLFWwindow* glfwWindow, int key, int /*scancode*/, int action, int mods) { + auto* window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + + // Handle key release events only + if (action != GLFW_RELEASE) { + return; + } + + switch (key) { + case GLFW_KEY_ESCAPE: { + glfwSetWindowShouldClose(glfwWindow, 1); + glfwPostEmptyEvent(); + } break; + case GLFW_KEY_X: + if (!mods) + window->map_->jumpTo(mbgl::CameraOptions().withCenter(mbgl::LatLng {}).withZoom(0.0).withBearing(0.0).withPitch(0.0)); + break; + default: + break; + } +} + +void Window::onMouseMove(GLFWwindow* glfwWindow, double x, double y) { + auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + if (window->tracking_) { + const double dx = x - window->lastX_; + const double dy = y - window->lastY_; + if (dx || dy) { + window->map_->moveBy(mbgl::ScreenCoordinate { dx, dy }, mbgl::nullopt); + } + } else if (window->rotating_) { + window->map_->rotateBy({ window->lastX_, window->lastY_ }, { x, y }, mbgl::nullopt); + } else if (window->pitching_) { + const double dy = y - window->lastY_; + if (dy) { + window->map_->pitchBy(dy / 2, mbgl::nullopt); + } + } + window->lastX_ = x; + window->lastY_ = y; +} + +void Window::onMouseClick(GLFWwindow* glfwWindow, int button, int action, int modifiers) { + auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + + if (button == GLFW_MOUSE_BUTTON_RIGHT || + (button == GLFW_MOUSE_BUTTON_LEFT && modifiers & GLFW_MOD_CONTROL)) { + window->rotating_ = action == GLFW_PRESS; + window->map_->setGestureInProgress(window->rotating_); + } else if (button == GLFW_MOUSE_BUTTON_LEFT && (modifiers & GLFW_MOD_SHIFT)) { + window->pitching_ = action == GLFW_PRESS; + window->map_->setGestureInProgress(window->pitching_); + } else if (button == GLFW_MOUSE_BUTTON_LEFT) { + window->tracking_ = action == GLFW_PRESS; + window->map_->setGestureInProgress(window->tracking_); + + if (action == GLFW_RELEASE) { + double now = glfwGetTime(); + if (now - window->lastClick_ < 0.4 /* ms */) { + if (modifiers & GLFW_MOD_SHIFT) { + window->map_->scaleBy(0.5, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::AnimationOptions{{mbgl::Milliseconds(500)}}); + } else { + window->map_->scaleBy(2.0, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::AnimationOptions{{mbgl::Milliseconds(500)}}); + } + } + window->lastClick_ = now; + } + } +} + +void Window::onWindowResize(GLFWwindow* glfwWindow, int width, int height) { + auto* window = reinterpret_cast<Window*>(glfwGetWindowUserPointer(glfwWindow)); + + window->mapSize_ = mbgl::Size{uint32_t(width), uint32_t(height)}; + window->map_->setSize(window->mapSize_); +} + +void Window::onScroll(GLFWwindow* glfwWindow, double /*xoffset*/, double yOffset) { + auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + double delta = yOffset * 40; + + bool isWheel = delta != 0 && std::fmod(delta, 4.000244140625) == 0; + + double absDelta = delta < 0 ? -delta : delta; + double scale = 2.0 / (1.0 + std::exp(-absDelta / 100.0)); + + // Make the scroll wheel a bit slower. + if (!isWheel) { + scale = (scale - 1.0) / 2.0 + 1.0; + } + + // Zooming out. + if (delta < 0 && scale != 0) { + scale = 1.0 / scale; + } + + window->map_->scaleBy(scale, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::nullopt); +} + +MapClient::MapClient(Window& window) : window_(window) {} +MapClient::~MapClient() = default; + +void MapClient::scheduleRepaint() { + window_.scheduleRepaint(); +} + +mapbox::map::MapClient::GLProcAddress MapClient::getGLProcAddress(const char* name) { + return glfwGetProcAddress(name); +} + +void Window::run() { + auto callback = [&] { + if (glfwWindowShouldClose(window_)) { + runLoop.stop(); + return; + } + + glfwPollEvents(); + render(); + }; + + frameTick.start(mbgl::Duration::zero(), mbgl::Milliseconds(1000 / 60), callback); + runLoop.run(); +} diff --git a/examples/glfw/window.hpp b/examples/glfw/window.hpp new file mode 100644 index 0000000000..d4a7d8f699 --- /dev/null +++ b/examples/glfw/window.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "map.hpp" +#include "mapbox/map/map_client.hpp" +#include "map_observer.hpp" +#include "map_change.hpp" +#include "map_load_error.hpp" + +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/timer.hpp> + +#include <atomic> +#include <iostream> +#include <memory> +#include <thread> + +struct GLFWwindow; + +class Observer : public mapbox::map::MapObserver { + void onMapChanged(mapbox::map::MapChange change) override { + if (change == mapbox::map::MapChange::DidFinishLoadingMap) + std::cout << "Map loading finished." << std::endl; + else if (change == mapbox::map::MapChange::DidFinishLoadingStyle) + std::cout << "Style loading finished." << std::endl; + } + + void onMapLoadError(mapbox::map::MapLoadError, const std::string& message) override { + std::cout << "Error: " << message << std::endl; + } +}; + +class Window { +public: + Window(); + virtual ~Window(); + + void render(); + void scheduleRepaint(); + bool shouldClose() const; + void run(); + +private: + bool needsRepaint_ = true; + mbgl::Size mapSize_{1024, 768}; + + // GLFW callbacks. + static void onKey(GLFWwindow *glfwWindow, int key, int, int, int); + static void onWindowResize(GLFWwindow *glfwWindow, int width, int height); + static void onMouseMove(GLFWwindow *glfwWindow, double x, double y); + static void onMouseClick(GLFWwindow *glfwWindow, int button, int action, int modifiers); + static void onScroll(GLFWwindow *glfwWindow, double xoffset, double yOffset); + + bool tracking_ = false; + bool rotating_ = false; + bool pitching_ = false; + + double lastX_ = 0, lastY_ = 0; + + double lastClick_ = -1; + + GLFWwindow *window_; + mbgl::util::RunLoop runLoop; + mbgl::util::Timer frameTick; + + std::unique_ptr<mapbox::map::Map> map_; +}; + +class MapClient : public mapbox::map::MapClient { +public: + MapClient(Window& window); + ~MapClient() override; + + void scheduleRepaint() final; + mapbox::map::MapClient::GLProcAddress getGLProcAddress(const char *name) final; + +private: + Window& window_; +}; diff --git a/include/mapbox/common/config.hpp b/include/mapbox/common/config.hpp new file mode 100644 index 0000000000..3c67af1f65 --- /dev/null +++ b/include/mapbox/common/config.hpp @@ -0,0 +1,26 @@ +#pragma once + +// Compiler defines for making symbols visible, otherwise they +// will be defined as hidden by default. + +// clang-format off + +#if defined WIN32 + #ifdef MAPBOX_MAP_BUILDING_LIB + #ifdef __GNUC__ + #define MAPBOX_MAP_PUBLIC __attribute__((dllexport)) + #else + #define MAPBOX_MAP_PUBLIC __declspec(dllexport) + #endif + #else + #ifdef __GNUC__ + #define MAPBOX_MAP_PUBLIC __attribute__((dllimport)) + #else + #define MAPBOX_MAP_PUBLIC __declspec(dllimport) + #endif + #endif +#else + #define MAPBOX_MAP_PUBLIC __attribute__((visibility ("default"))) +#endif + +// clang-format on diff --git a/include/mapbox/common/optional.hpp b/include/mapbox/common/optional.hpp new file mode 100644 index 0000000000..e5f7e71584 --- /dev/null +++ b/include/mapbox/common/optional.hpp @@ -0,0 +1,27 @@ +#pragma once + +#if __cplusplus >= 201703L +#define WITH_OPTIONALS +#endif + +#if defined(WITH_OPTIONALS) +#include <optional> +#else +#include <experimental/optional> +#endif + +namespace mapbox { +namespace common { + +#if defined(WITH_OPTIONALS) +using nullopt_t = std::nullopt_t; +constexpr nullopt_t nullopt = std::nullopt; +template<class T> using optional = std::optional<T>; +#else +using nullopt_t = std::experimental::nullopt_t; +constexpr nullopt_t nullopt = std::experimental::nullopt; +template<class T> using optional = std::experimental::optional<T>; +#endif + +} // common +} // mapbox diff --git a/include/mapbox/common/peer.hpp b/include/mapbox/common/peer.hpp new file mode 100644 index 0000000000..b00889ca8d --- /dev/null +++ b/include/mapbox/common/peer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <memory> +#include <type_traits> +#include <utility> + +namespace mapbox { +namespace common { + +class peer { +public: + peer() = default; + + template <class T> + peer(T&& value) : ptr(std::make_unique<DataHolder<T>>(std::forward<T>(value))) {} + + bool has_value() const { return static_cast<bool>(ptr); } + + template <class T> + T& get() { + return static_cast<DataHolder<T>*>(ptr.get())->data; + } +private: + struct DataHolderBase { + virtual ~DataHolderBase() = default; + }; + + template <typename T> + struct DataHolder : public DataHolderBase { + DataHolder(T&& data_) : data(std::forward<T>(data_)) {} + typename std::decay<T>::type data; + }; + std::unique_ptr<DataHolderBase> ptr; +}; + +} // namespace common +} // namespace mapbox diff --git a/include/mapbox/map/constrain_mode.hpp b/include/mapbox/map/constrain_mode.hpp new file mode 100644 index 0000000000..ca942b8a2e --- /dev/null +++ b/include/mapbox/map/constrain_mode.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class ConstrainMode { + None, + HeightOnly, + WidthAndHeight +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map.hpp b/include/mapbox/map/map.hpp new file mode 100644 index 0000000000..9acd653589 --- /dev/null +++ b/include/mapbox/map/map.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +#include "constrain_mode.hpp" +#include "mbgl/annotation/annotation.hpp" +#include "mbgl/map/bound_options.hpp" +#include "mbgl/map/camera.hpp" +#include "mbgl/map/map_options.hpp" +#include "mbgl/map/mode.hpp" +#include "mbgl/map/projection_mode.hpp" +#include "mbgl/storage/resource_options.hpp" +#include "mbgl/util/geo.hpp" +#include "mbgl/util/size.hpp" +#include "north_orientation.hpp" +#include "still_image_callback.hpp" +#include "viewport_mode.hpp" +#include <cstdint> +#include <mapbox/common/optional.hpp> +#include <mapbox/common/peer.hpp> +#include <memory> +#include <string> +#include <vector> + +namespace mapbox { +namespace map { + +class MapImpl; + +class MapClient; +class MapObserver; + +class MAPBOX_MAP_PUBLIC Map { +public: + Map(std::shared_ptr<MapClient> client, std::shared_ptr<MapObserver> observer, ::mbgl::MapOptions mapOptions, ::mbgl::ResourceOptions resourceOptions); + ~Map(); + + void createRenderer(); + void destroyRenderer(); + void render(); + void setSize(const ::mbgl::Size& size); + void setStyleURL(const std::string& styleURL); + void setDefaultFramebufferObject(uint32_t framebufferId); + void renderStill(StillImageCallback callback); + void renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback); + void triggerRepaint(); + void jumpTo(const ::mbgl::CameraOptions& camera); + ::mbgl::CameraOptions getCameraOptions(const ::mbgl::EdgeInsets& padding); + void easeTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation); + void flyTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation); + void moveBy(const ::mbgl::ScreenCoordinate& point, const mapbox::common::optional<::mbgl::AnimationOptions>& animation); + void scaleBy(double scale, const mapbox::common::optional<::mbgl::ScreenCoordinate>& anchor, const mapbox::common::optional<::mbgl::AnimationOptions>& animation); + void pitchBy(double pitch, const mapbox::common::optional<::mbgl::AnimationOptions>& animation); + void rotateBy(const ::mbgl::ScreenCoordinate& first, const ::mbgl::ScreenCoordinate& second, const mapbox::common::optional<::mbgl::AnimationOptions>& animation); + ::mbgl::CameraOptions cameraForLatLngBounds(const ::mbgl::LatLngBounds& bounds, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch); + ::mbgl::CameraOptions cameraForLatLngs(const std::vector<::mbgl::LatLng>& latlng, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch); + ::mbgl::LatLngBounds latLngBoundsForCamera(const ::mbgl::CameraOptions& camera); + void setProjectionMode(const ::mbgl::ProjectionMode& mode); + ::mbgl::ProjectionMode getProjectionMode(); + void cancelTransitions(); + void setGestureInProgress(bool inProgress); + bool isGestureInProgress(); + bool isRotating(); + bool isScaling(); + bool isPanning(); + void setBounds(const ::mbgl::BoundOptions& options); + ::mbgl::BoundOptions getBounds(); + void setPrefetchZoomDelta(uint8_t delta); + uint8_t getPrefetchZoomDelta(); + void setNorthOrientation(NorthOrientation orientation); + void setConstrainMode(ConstrainMode mode); + void setViewportMode(ViewportMode mode); + ::mbgl::MapOptions getMapOptions(); + ::mbgl::ScreenCoordinate pixelForLatLng(const ::mbgl::LatLng& latLng); + ::mbgl::LatLng latLngForPixel(const ::mbgl::ScreenCoordinate& pixel); + void removeAnnotationImage(const std::string& id); + double getTopOffsetPixelsForAnnotationImage(const std::string& id); + uint64_t addAnnotation(const ::mbgl::Annotation& annotation); + void updateAnnotation(uint64_t id, const ::mbgl::Annotation& annotation); + void removeAnnotation(uint64_t id); + void setDebug(const ::mbgl::MapDebugOptions& debugOptions); + void cycleDebugOptions(); + ::mbgl::MapDebugOptions getDebug(); + bool isFullyLoaded(); + void dumpDebugLogs(); + +private: + using Impl = MapImpl; + std::unique_ptr<Impl> impl; + +public: + /// @cond For use only by generated bindings. + mapbox::common::peer peer; + /// @endcond +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map_change.hpp b/include/mapbox/map/map_change.hpp new file mode 100644 index 0000000000..9bde7aee73 --- /dev/null +++ b/include/mapbox/map/map_change.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class MapChange { + DidFinishLoadingMap, + DidFinishLoadingStyle +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map_client.hpp b/include/mapbox/map/map_client.hpp new file mode 100644 index 0000000000..21fa6ba176 --- /dev/null +++ b/include/mapbox/map/map_client.hpp @@ -0,0 +1,35 @@ +#pragma once + +namespace mapbox { +namespace map { + +/** +* @brief Interface that must be implement by a Map client. +*/ +class MapClient { +public: + /** + * @brief Alias for an opaque function pointer. + */ + using GLProcAddress = void (*)(); + + /** + * @brief Returns the GL function pointer address for a given name. + * + * @param name a string containing a GL function name. + * @return GLProcAddress an opaque function pointer for a GL function. + */ + virtual GLProcAddress getGLProcAddress(const char* name) = 0; + + /** + * @brief Notifies the client to schedule a repaint. This must eventually trigger a Map::render() call, + * otherwise the map will be left in incomplete state. + */ + virtual void scheduleRepaint() = 0; + +protected: + virtual ~MapClient() = default; +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map_load_error.hpp b/include/mapbox/map/map_load_error.hpp new file mode 100644 index 0000000000..0dfb3e2093 --- /dev/null +++ b/include/mapbox/map/map_load_error.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class MapLoadError { + StyleParseError, + StyleLoadError, + NotFoundError, + UnknownError +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map_mode.hpp b/include/mapbox/map/map_mode.hpp new file mode 100644 index 0000000000..bd0e5af96e --- /dev/null +++ b/include/mapbox/map/map_mode.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class MapMode { + Continuous, + Static, + Tile +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/map_observer.hpp b/include/mapbox/map/map_observer.hpp new file mode 100644 index 0000000000..429e3c3ee1 --- /dev/null +++ b/include/mapbox/map/map_observer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "map_change.hpp" +#include "map_load_error.hpp" +#include <mapbox/common/peer.hpp> +#include <string> + +namespace mapbox { +namespace map { + +class MapObserver { +public: + virtual void onMapChanged(MapChange change) = 0; + virtual void onMapLoadError(MapLoadError error, const std::string& message) = 0; + +protected: + virtual ~MapObserver() = default; + +public: + /// @cond For use only by generated bindings. + mapbox::common::peer peer; + /// @endcond +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/north_orientation.hpp b/include/mapbox/map/north_orientation.hpp new file mode 100644 index 0000000000..6f5dd0e556 --- /dev/null +++ b/include/mapbox/map/north_orientation.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class NorthOrientation { + Upwards, + Rightwards, + Downwards, + Leftwards +}; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/still_image_callback.hpp b/include/mapbox/map/still_image_callback.hpp new file mode 100644 index 0000000000..20f58cebfe --- /dev/null +++ b/include/mapbox/map/still_image_callback.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <functional> +#include <mapbox/common/optional.hpp> +#include <string> + +namespace mapbox { +namespace map { + +using StillImageCallback = std::function<void(const mapbox::common::optional<std::string>&)>; + +} // namespace map +} // namespace mapbox diff --git a/include/mapbox/map/viewport_mode.hpp b/include/mapbox/map/viewport_mode.hpp new file mode 100644 index 0000000000..2badc3a075 --- /dev/null +++ b/include/mapbox/map/viewport_mode.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <mapbox/common/config.hpp> + +namespace mapbox { +namespace map { + +enum class ViewportMode { + Default, + FlippedY +}; + +} // namespace map +} // namespace mapbox diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index 7cc1f1fe4d..532c3a50ad 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -99,7 +99,6 @@ macro(mbgl_platform_core) endif() endmacro() - macro(mbgl_filesource) # Modify platform/linux/filesource-files.json to change the source files for this target. target_sources_from_file(mbgl-filesource PRIVATE platform/linux/filesource-files.json) @@ -127,6 +126,34 @@ macro(mbgl_platform_glfw) ) endmacro() +macro(mapbox_glfw_app) + target_sources(mbgl-core + # Snapshotting + PRIVATE src/mapbox/map/map.cpp + PRIVATE src/mapbox/map/map_impl.cpp + PRIVATE src/mapbox/map/renderer_backend.cpp + PRIVATE src/mapbox/map/scheduler.cpp + ) + + target_include_directories(mbgl-core + PRIVATE include + PRIVATE include/mapbox/ + PRIVATE include/mapbox/map + PRIVATE src/ + ) + + target_link_libraries(mapbox-glfw-app + PRIVATE mbgl-filesource + PRIVATE mbgl-loop-uv + ) + + add_custom_command( + TARGET mapbox-glfw-app POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/misc/ca-bundle.crt + ${CMAKE_CURRENT_BINARY_DIR}/ca-bundle.crt + ) +endmacro() macro(mbgl_platform_render) target_link_libraries(mbgl-render diff --git a/src/mapbox/map/map.cpp b/src/mapbox/map/map.cpp new file mode 100644 index 0000000000..a0df8a948f --- /dev/null +++ b/src/mapbox/map/map.cpp @@ -0,0 +1,210 @@ +#include <mapbox/common/config.hpp> + +#include "map.hpp" +#include "map_impl.hpp" + +#include <utility> + +namespace mapbox { +namespace map { + +Map::Map(std::shared_ptr<MapClient> client, std::shared_ptr<MapObserver> observer, ::mbgl::MapOptions mapOptions, ::mbgl::ResourceOptions resourceOptions) +: impl(std::make_unique<Impl>(std::move(client), std::move(observer), std::move(mapOptions), std::move(resourceOptions))) +{} + +Map::~Map() = default; + +void Map::createRenderer() { + impl->createRenderer(); +} + +void Map::destroyRenderer() { + impl->destroyRenderer(); +} + +void Map::render() { + impl->render(); +} + +void Map::setSize(const ::mbgl::Size& size) { + impl->setSize(size); +} + +void Map::setStyleURL(const std::string& styleURL) { + impl->setStyleURL(styleURL); +} + +void Map::setDefaultFramebufferObject(uint32_t framebufferId) { + impl->setDefaultFramebufferObject(framebufferId); +} + +void Map::renderStill(StillImageCallback callback) { + impl->renderStill(std::move(callback)); +} + +void Map::renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback) { + impl->renderStill(camera, options, std::move(callback)); +} + +void Map::triggerRepaint() { + impl->triggerRepaint(); +} + +void Map::jumpTo(const ::mbgl::CameraOptions& camera) { + impl->jumpTo(camera); +} + +::mbgl::CameraOptions Map::getCameraOptions(const ::mbgl::EdgeInsets& padding) { + return impl->getCameraOptions(padding); +} + +void Map::easeTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation) { + impl->easeTo(camera, animation); +} + +void Map::flyTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation) { + impl->flyTo(camera, animation); +} + +void Map::moveBy(const ::mbgl::ScreenCoordinate& point, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) { + impl->moveBy(point, animation); +} + +void Map::scaleBy(double scale, const mapbox::common::optional<::mbgl::ScreenCoordinate>& anchor, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) { + impl->scaleBy(scale, anchor, animation); +} + +void Map::pitchBy(double pitch, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) { + impl->pitchBy(pitch, animation); +} + +void Map::rotateBy(const ::mbgl::ScreenCoordinate& first, const ::mbgl::ScreenCoordinate& second, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) { + impl->rotateBy(first, second, animation); +} + +::mbgl::CameraOptions Map::cameraForLatLngBounds(const ::mbgl::LatLngBounds& bounds, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch) { + return impl->cameraForLatLngBounds(bounds, padding, bearing, pitch); +} + +::mbgl::CameraOptions Map::cameraForLatLngs(const std::vector<::mbgl::LatLng>& latlng, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch) { + return impl->cameraForLatLngs(latlng, padding, bearing, pitch); +} + +::mbgl::LatLngBounds Map::latLngBoundsForCamera(const ::mbgl::CameraOptions& camera) { + return impl->latLngBoundsForCamera(camera); +} + +void Map::setProjectionMode(const ::mbgl::ProjectionMode& mode) { + impl->setProjectionMode(mode); +} + +::mbgl::ProjectionMode Map::getProjectionMode() { + return impl->getProjectionMode(); +} + +void Map::cancelTransitions() { + impl->cancelTransitions(); +} + +void Map::setGestureInProgress(bool inProgress) { + impl->setGestureInProgress(inProgress); +} + +bool Map::isGestureInProgress() { + return impl->isGestureInProgress(); +} + +bool Map::isRotating() { + return impl->isRotating(); +} + +bool Map::isScaling() { + return impl->isScaling(); +} + +bool Map::isPanning() { + return impl->isPanning(); +} + +void Map::setBounds(const ::mbgl::BoundOptions& options) { + impl->setBounds(options); +} + +::mbgl::BoundOptions Map::getBounds() { + return impl->getBounds(); +} + +void Map::setPrefetchZoomDelta(uint8_t delta) { + impl->setPrefetchZoomDelta(delta); +} + +uint8_t Map::getPrefetchZoomDelta() { + return impl->getPrefetchZoomDelta(); +} + +void Map::setNorthOrientation(NorthOrientation orientation) { + impl->setNorthOrientation(orientation); +} + +void Map::setConstrainMode(ConstrainMode mode) { + impl->setConstrainMode(mode); +} + +void Map::setViewportMode(ViewportMode mode) { + impl->setViewportMode(mode); +} + +::mbgl::MapOptions Map::getMapOptions() { + return impl->getMapOptions(); +} + +::mbgl::ScreenCoordinate Map::pixelForLatLng(const ::mbgl::LatLng& latLng) { + return impl->pixelForLatLng(latLng); +} + +::mbgl::LatLng Map::latLngForPixel(const ::mbgl::ScreenCoordinate& pixel) { + return impl->latLngForPixel(pixel); +} + +void Map::removeAnnotationImage(const std::string& id) { + impl->removeAnnotationImage(id); +} + +double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) { + return impl->getTopOffsetPixelsForAnnotationImage(id); +} + +uint64_t Map::addAnnotation(const ::mbgl::Annotation& annotation) { + return impl->addAnnotation(annotation); +} + +void Map::updateAnnotation(uint64_t id, const ::mbgl::Annotation& annotation) { + impl->updateAnnotation(id, annotation); +} + +void Map::removeAnnotation(uint64_t id) { + impl->removeAnnotation(id); +} + +void Map::setDebug(const ::mbgl::MapDebugOptions& debugOptions) { + impl->setDebug(debugOptions); +} + +void Map::cycleDebugOptions() { + impl->cycleDebugOptions(); +} + +::mbgl::MapDebugOptions Map::getDebug() { + return impl->getDebug(); +} + +bool Map::isFullyLoaded() { + return impl->isFullyLoaded(); +} + +void Map::dumpDebugLogs() { + impl->dumpDebugLogs(); +} + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/map_impl.cpp b/src/mapbox/map/map_impl.cpp new file mode 100644 index 0000000000..6150da8b6c --- /dev/null +++ b/src/mapbox/map/map_impl.cpp @@ -0,0 +1,416 @@ +#include <mapbox/common/optional.hpp> +#include <mapbox/map/map_impl.hpp> +#include <mapbox/map/scheduler.hpp> + +#include <mbgl/gfx/backend_scope.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/platform/gl_functions.hpp> +#include <mbgl/map/map_options.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/resource_options.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/util/premultiply.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/traits.hpp> + +#include "map_load_error.hpp" + +#include <algorithm> +#include <cassert> +#include <functional> +#include <iterator> +#include <memory> + +#ifndef GL_VIEWPORT +#define GL_VIEWPORT 0x0BA2 +#endif + +namespace mapbox { +namespace map { + +// MapLoadError +static_assert(mbgl::underlying_type(MapLoadError::StyleLoadError) == mbgl::underlying_type(mbgl::MapLoadError::StyleLoadError), "error"); +static_assert(mbgl::underlying_type(MapLoadError::StyleParseError) == mbgl::underlying_type(mbgl::MapLoadError::StyleParseError), "error"); +static_assert(mbgl::underlying_type(MapLoadError::NotFoundError) == mbgl::underlying_type(mbgl::MapLoadError::NotFoundError), "error"); +static_assert(mbgl::underlying_type(MapLoadError::UnknownError) == mbgl::underlying_type(mbgl::MapLoadError::UnknownError), "error"); + +// MapMode +static_assert(mbgl::underlying_type(MapMode::Continuous) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error"); +static_assert(mbgl::underlying_type(MapMode::Static) == mbgl::underlying_type(mbgl::MapMode::Static), "error"); +static_assert(mbgl::underlying_type(MapMode::Tile) == mbgl::underlying_type(mbgl::MapMode::Tile), "error"); + +// ConstrainMode +static_assert(mbgl::underlying_type(ConstrainMode::None) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error"); +static_assert(mbgl::underlying_type(ConstrainMode::HeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error"); +static_assert(mbgl::underlying_type(ConstrainMode::WidthAndHeight) == mbgl::underlying_type(mbgl::ConstrainMode::WidthAndHeight), "error"); + +// ViewportMode +static_assert(mbgl::underlying_type(ViewportMode::Default) == mbgl::underlying_type(mbgl::ViewportMode::Default), "error"); +static_assert(mbgl::underlying_type(ViewportMode::FlippedY) == mbgl::underlying_type(mbgl::ViewportMode::FlippedY), "error"); + +// NorthOrientation +static_assert(mbgl::underlying_type(NorthOrientation::Upwards) == mbgl::underlying_type(mbgl::NorthOrientation::Upwards), "error"); +static_assert(mbgl::underlying_type(NorthOrientation::Rightwards) == mbgl::underlying_type(mbgl::NorthOrientation::Rightwards), "error"); +static_assert(mbgl::underlying_type(NorthOrientation::Downwards) == mbgl::underlying_type(mbgl::NorthOrientation::Downwards), "error"); +static_assert(mbgl::underlying_type(NorthOrientation::Leftwards) == mbgl::underlying_type(mbgl::NorthOrientation::Leftwards), "error"); + +namespace { + +Scheduler* getScheduler() { + static thread_local std::shared_ptr<Scheduler> scheduler = std::make_shared<Scheduler>(); + return scheduler.get(); +} + +} // namespace + +MapImpl::MapImpl(std::shared_ptr<MapClient> client, + std::shared_ptr<mapbox::map::MapObserver> observer, + const mbgl::MapOptions& mapOptions, + const mbgl::ResourceOptions& resourceOptions) + : size_(mapOptions.size()), pixelRatio_(mapOptions.pixelRatio()) + , observer_(std::move(observer)) + , client_(std::move(client)) { + map_ = std::make_unique<mbgl::Map>(*this, *this, mapOptions, resourceOptions); +} + +MapImpl::~MapImpl() = default; + +void MapImpl::createRenderer() { + assert(!rendererBackend_); + assert(!renderer_); + + renderThreadId_ = std::make_unique<std::thread::id>(std::this_thread::get_id()); + + rendererBackend_ = std::make_unique<RendererBackend>( + std::bind(&MapClient::getGLProcAddress, client_, std::placeholders::_1), + mbgl::Size{uint32_t(size_.width * pixelRatio_), uint32_t(size_.height * pixelRatio_)}, defaultFramebufferID_); + + renderer_ = std::make_unique<mbgl::Renderer>(*rendererBackend_, pixelRatio_); + + renderer_->setObserver(rendererObserver_); + + schedulerNotify_ = std::make_shared<Scheduler::NotifyFn>([this] { client_->scheduleRepaint(); }); + getScheduler()->attach(schedulerNotify_); + mbgl::Scheduler::SetCurrent(getScheduler()); +} + +void MapImpl::destroyRenderer() { + assert(*renderThreadId_ == std::this_thread::get_id()); + schedulerNotify_.reset(); + renderer_.reset(); + rendererBackend_.reset(); +} + +void MapImpl::render() { + assert(!renderThreadId_ || (*renderThreadId_ == std::this_thread::get_id())); + mbgl::gfx::BackendScope scope(*rendererBackend_, mbgl::gfx::BackendScope::ScopeType::Implicit); + + std::shared_ptr<mbgl::UpdateParameters> params; + { + // Lock on the parameters + std::lock_guard<std::mutex> lock(updateMutex_); + + if (!updateParameters_) { + return; + } + + // Hold on to the update parameters during render + params = updateParameters_; + } + + if (params) { + if (!renderer_) { + createRenderer(); + } + + assert(*renderThreadId_ == std::this_thread::get_id()); + { + std::lock_guard<std::mutex> lock(resizeMutex_); + + mbgl::platform::GLint viewport[4]; + mbgl::platform::glGetIntegerv(GL_VIEWPORT, viewport); + x_ = viewport[0]; + y_ = viewport[1]; + rendererBackend_->move(x_, y_); + + if (sizeChanged_) { + rendererBackend_->resize(mbgl::Size(static_cast<uint32_t>(size_.width * pixelRatio_), + static_cast<uint32_t>(size_.height * pixelRatio_))); + rendererBackend_->bind(); + sizeChanged_ = false; + } + renderer_->render(*params); + } + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) + static_cast<Scheduler*>(mbgl::Scheduler::GetCurrent())->processEvents(); +} + +void MapImpl::setSize(const ::mbgl::Size& size) { + std::lock_guard<std::mutex> lock(resizeMutex_); + size_ = size; + sizeChanged_ = true; + + map_->setSize(size_); +} + +void MapImpl::setDefaultFramebufferObject(uint32_t framebufferID) { + defaultFramebufferID_ = framebufferID; + if (rendererBackend_) { + rendererBackend_->setDefaultFramebufferObject(defaultFramebufferID_); + } +} + +void MapImpl::setStyleURL(const std::string& styleURL) { + map_->getStyle().loadURL(styleURL); +} + +void MapImpl::triggerRepaint() { + map_->triggerRepaint(); +} + +void MapImpl::renderStill(StillImageCallback callback) { + map_->renderStill([cb = std::move(callback)](const std::exception_ptr eptr) { + mapbox::common::optional<std::string> error; + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch(const std::exception& e) { + error = std::string(e.what()); + } + + if (cb) { + cb(error); + } + }); +} + +void MapImpl::renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback) { + map_->renderStill(camera, options, [cb = std::move(callback)](const std::exception_ptr eptr) { + mapbox::common::optional<std::string> error; + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch(const std::exception& e) { + error = std::string(e.what()); + } + + if (cb) { + cb(error); + } + }); +} + +mbgl::CameraOptions MapImpl::getCameraOptions(const mbgl::EdgeInsets& padding) const { + return map_->getCameraOptions(padding); +} + +void MapImpl::jumpTo(const mbgl::CameraOptions& camera) { + try { + map_->jumpTo(camera); + } catch (const std::exception& e) { + } +} + +void MapImpl::easeTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation) { + map_->easeTo(camera, animation); +} + +void MapImpl::flyTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation) { + map_->flyTo(camera, animation); +} + +void MapImpl::moveBy(const mbgl::ScreenCoordinate& point, const std::experimental::optional<::mbgl::AnimationOptions>& animation) { + map_->moveBy(point, animation.value_or(mbgl::AnimationOptions{})); +} + +void MapImpl::scaleBy(double scale, const std::experimental::optional<mbgl::ScreenCoordinate>& anchor, const std::experimental::optional<::mbgl::AnimationOptions>& animation){ + map_->scaleBy(scale, anchor, animation.value_or(mbgl::AnimationOptions{})); +} + +void MapImpl::pitchBy(double pitch, const std::experimental::optional<::mbgl::AnimationOptions>& animation) { + map_->pitchBy(pitch, animation.value_or(mbgl::AnimationOptions{})); +} + +void MapImpl::rotateBy(const mbgl::ScreenCoordinate& first, const mbgl::ScreenCoordinate& second, const std::experimental::optional<::mbgl::AnimationOptions>& animation) { + map_->rotateBy(first, second, animation.value_or(mbgl::AnimationOptions{})); +} + +mbgl::CameraOptions MapImpl::cameraForLatLngBounds(const mbgl::LatLngBounds& bounds, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch) { + return map_->cameraForLatLngBounds(bounds, padding, bearing, pitch); +} + +mbgl::CameraOptions MapImpl::cameraForLatLngs(const std::vector<mbgl::LatLng>& latlng, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch) { + return map_->cameraForLatLngs(latlng, padding, bearing, pitch); +} + +mbgl::LatLngBounds MapImpl::latLngBoundsForCamera(const mbgl::CameraOptions& camera) { + return map_->latLngBoundsForCamera(camera); +} + +void MapImpl::setProjectionMode(const mbgl::ProjectionMode& options) { + map_->setProjectionMode(options); +} + +mbgl::ProjectionMode MapImpl::getProjectionMode() const { + return map_->getProjectionMode(); +} + +void MapImpl::cancelTransitions() { + map_->cancelTransitions(); +} + +void MapImpl::setGestureInProgress(bool inProgress) { + map_->setGestureInProgress(inProgress); +} + +bool MapImpl::isGestureInProgress() const { + return map_->isGestureInProgress(); +} + +bool MapImpl::isRotating() const { + return map_->isRotating(); +} + +bool MapImpl::isScaling() const { + return map_->isScaling(); +} + +bool MapImpl::isPanning() const { + return map_->isPanning(); +} + +void MapImpl::setBounds(const ::mbgl::BoundOptions& options) { + map_->setBounds(options); +} + +::mbgl::BoundOptions MapImpl::getBounds() const { + return map_->getBounds(); +} + +void MapImpl::setPrefetchZoomDelta(uint8_t delta) { + map_->setPrefetchZoomDelta(delta); +} + +uint8_t MapImpl::getPrefetchZoomDelta() const { + return map_->getPrefetchZoomDelta(); +} + +void MapImpl::setNorthOrientation(NorthOrientation orientation) { + map_->setNorthOrientation(static_cast<mbgl::NorthOrientation>(orientation)); +} + +void MapImpl::setConstrainMode(ConstrainMode mode) { + map_->setConstrainMode(static_cast<mbgl::ConstrainMode>(mode)); +} + +void MapImpl::setViewportMode(ViewportMode mode) { + map_->setViewportMode(static_cast<mbgl::ViewportMode>(mode)); +} + +mbgl::MapOptions MapImpl::getMapOptions() const { + return map_->getMapOptions(); +} + +mbgl::ScreenCoordinate MapImpl::pixelForLatLng(const mbgl::LatLng& latLng) { + return map_->pixelForLatLng(latLng); +} + +mbgl::LatLng MapImpl::latLngForPixel(const mbgl::ScreenCoordinate& pixel) { + return map_->latLngForPixel(pixel); +} + +void MapImpl::removeAnnotationImage(const std::string& id) { + map_->removeAnnotationImage(id); +} + +double MapImpl::getTopOffsetPixelsForAnnotationImage(const std::string& id) { + return map_->getTopOffsetPixelsForAnnotationImage(id); +} + +mbgl::AnnotationID MapImpl::addAnnotation(const mbgl::Annotation& annotation) { + return map_->addAnnotation(annotation); +} + +void MapImpl::updateAnnotation(uint64_t id, const mbgl::Annotation& annotation) { + map_->updateAnnotation(id, annotation); +} + +void MapImpl::removeAnnotation(uint64_t id) { + map_->removeAnnotation(id); +} + +void MapImpl::setDebug(const mbgl::MapDebugOptions& debugOptions) { + map_->setDebug(debugOptions); +} + +void MapImpl::cycleDebugOptions() { + map_->cycleDebugOptions(); +} + +mbgl::MapDebugOptions MapImpl::getDebug() const { + return map_->getDebug(); +} + +bool MapImpl::isFullyLoaded() const { + return map_->isFullyLoaded(); +} + +void MapImpl::dumpDebugLogs() const { + map_->dumpDebugLogs(); +} + +void MapImpl::reset() { + assert(clientThreadId_ == std::this_thread::get_id()); +} + +void MapImpl::setObserver(mbgl::RendererObserver& observer) { + rendererObserver_ = &observer; + if (renderer_) { + renderer_->setObserver(&observer); + } +} + +void MapImpl::update(std::shared_ptr<mbgl::UpdateParameters> updateParameters) { + std::lock_guard<std::mutex> lock(updateMutex_); + updateParameters_ = std::move(updateParameters); + client_->scheduleRepaint(); +} + +void MapImpl::onCameraWillChange(CameraChangeMode) {} + +void MapImpl::onCameraIsChanging() {} + +void MapImpl::onCameraDidChange(CameraChangeMode) {} + +void MapImpl::onWillStartLoadingMap() {} + +void MapImpl::onDidFinishLoadingMap() { + observer_->onMapChanged(MapChange::DidFinishLoadingMap); +} + +void MapImpl::onDidFailLoadingMap(mbgl::MapLoadError error, const std::string& message) { + observer_->onMapLoadError(static_cast<MapLoadError>(error), message); +} + +void MapImpl::onWillStartRenderingFrame() {} + +void MapImpl::onDidFinishRenderingFrame(RenderMode) {} + +void MapImpl::onWillStartRenderingMap() {} + +void MapImpl::onDidFinishRenderingMap(RenderMode) {} + +void MapImpl::onDidFinishLoadingStyle() { + observer_->onMapChanged(MapChange::DidFinishLoadingStyle); +} + +void MapImpl::onSourceChanged(mbgl::style::Source&) {} + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/map_impl.hpp b/src/mapbox/map/map_impl.hpp new file mode 100644 index 0000000000..796820c1e9 --- /dev/null +++ b/src/mapbox/map/map_impl.hpp @@ -0,0 +1,180 @@ +#pragma once + +#include <mapbox/map/map_client.hpp> +#include <mapbox/map/renderer_backend.hpp> +#include <mapbox/map/scheduler.hpp> + +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/map/bound_options.hpp> +#include <mbgl/map/map_observer.hpp> +#include <mbgl/map/map_options.hpp> +#include <mbgl/map/projection_mode.hpp> +#include <mbgl/storage/resource_options.hpp> +#include <mbgl/map/camera.hpp> +#include <mbgl/renderer/renderer_frontend.hpp> +#include <mbgl/util/size.hpp> + +#include "constrain_mode.hpp" +#include "map_mode.hpp" +#include "map_observer.hpp" +#include "north_orientation.hpp" +#include "renderer_backend.hpp" +#include "still_image_callback.hpp" +#include "viewport_mode.hpp" + +#include <future> +#include <memory> +#include <mutex> +#include <string> +#include <thread> +#include <vector> + +namespace mbgl { +class Map; +class Renderer; +class RendererObserver; +class UpdateParameters; + +namespace style { +class Layer; +} //namespace style + +namespace util { +class RunLoop; +} // namespace util +} // namespace mbgl + +namespace mapbox { +namespace map { + +class MapImpl : public mbgl::RendererFrontend, public mbgl::MapObserver { +public: + MapImpl(std::shared_ptr<MapClient> client, + std::shared_ptr<mapbox::map::MapObserver> observer, + const mbgl::MapOptions& mapOptions, + const mbgl::ResourceOptions& resourceOptions); + ~MapImpl() override; + + // Called from the render thread - these functions are _not_ thread-safe. + void createRenderer(); + void destroyRenderer(); + void render(); + void setSize(const mbgl::Size& size); + void setDefaultFramebufferObject(uint32_t); + + void renderStill(StillImageCallback callback); + void renderStill(const mbgl::CameraOptions& camera, const mbgl::MapDebugOptions& options, StillImageCallback callback); + + // Triggers a repaint. + void triggerRepaint(); + + void jumpTo(const mbgl::CameraOptions& camera); + mbgl::CameraOptions getCameraOptions(const mbgl::EdgeInsets& padding) const; + + void easeTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation); + void flyTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation); + void moveBy(const mbgl::ScreenCoordinate& point, const std::experimental::optional<::mbgl::AnimationOptions>& animation); + void scaleBy(double scale, const std::experimental::optional<mbgl::ScreenCoordinate>& anchor, const std::experimental::optional<::mbgl::AnimationOptions>& animation); + void pitchBy(double pitch, const std::experimental::optional<::mbgl::AnimationOptions>& animation); + void rotateBy(const mbgl::ScreenCoordinate& first, const mbgl::ScreenCoordinate& second, const std::experimental::optional<::mbgl::AnimationOptions>& animation); + mbgl::CameraOptions cameraForLatLngBounds(const mbgl::LatLngBounds& bounds, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch); + mbgl::CameraOptions cameraForLatLngs(const std::vector<mbgl::LatLng>& latlng, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch); + mbgl::LatLngBounds latLngBoundsForCamera(const mbgl::CameraOptions& cameraOptions); + + void setProjectionMode(const mbgl::ProjectionMode& mode); + mbgl::ProjectionMode getProjectionMode() const; + + // Transition + void cancelTransitions(); + void setGestureInProgress(bool); + bool isGestureInProgress() const; + bool isRotating() const; + bool isScaling() const; + bool isPanning() const; + + void setBounds(const mbgl::BoundOptions& options); + /// Returns the current map bound options. All optional fields in BoundOptions are set. + mbgl::BoundOptions getBounds() const; + + // Tile prefetching + void setPrefetchZoomDelta(uint8_t delta); + uint8_t getPrefetchZoomDelta() const; + + // Map options + void setNorthOrientation(NorthOrientation orientation); + void setConstrainMode(ConstrainMode mode); + void setViewportMode(ViewportMode mode); + mbgl::MapOptions getMapOptions() const; + + // Thread safe + void setStyleURL(const std::string& styleURL); + + // Projection + mbgl::ScreenCoordinate pixelForLatLng(const mbgl::LatLng& latLng); + mbgl::LatLng latLngForPixel(const mbgl::ScreenCoordinate& pixel); + + // Annotations + void removeAnnotationImage(const std::string& id); + double getTopOffsetPixelsForAnnotationImage(const std::string& id); + + uint64_t addAnnotation(const mbgl::Annotation& annotation); + void updateAnnotation(uint64_t id, const mbgl::Annotation& annotation); + void removeAnnotation(uint64_t id); + + // Debug + void setDebug(const mbgl::MapDebugOptions& debugOptions); + void cycleDebugOptions(); + mbgl::MapDebugOptions getDebug() const; + + bool isFullyLoaded() const; + void dumpDebugLogs() const; + +private: + // mbgl::RendererFrontend implementation. + void reset() final; + void setObserver(mbgl::RendererObserver& observer) final; + void update(std::shared_ptr<mbgl::UpdateParameters>) final; + + // mbgl::MapObserver implementation. + void onCameraWillChange(CameraChangeMode) final; + void onCameraIsChanging() final; + void onCameraDidChange(CameraChangeMode) final; + void onWillStartLoadingMap() final; + void onDidFinishLoadingMap() final; + void onDidFailLoadingMap(mbgl::MapLoadError, const std::string&) final; + void onWillStartRenderingFrame() final; + void onDidFinishRenderingFrame(RenderMode) final; + void onWillStartRenderingMap() final; + void onDidFinishRenderingMap(RenderMode) final; + void onDidFinishLoadingStyle() final; + void onSourceChanged(mbgl::style::Source&) final; + + std::mutex resizeMutex_; + mbgl::Size size_; + bool sizeChanged_ = true; + double pixelRatio_; + + std::thread::id clientThreadId_ = std::this_thread::get_id(); + std::unique_ptr<std::thread::id> renderThreadId_; + + mutable std::mutex updateMutex_; + std::shared_ptr<mbgl::UpdateParameters> updateParameters_; + std::unique_ptr<mbgl::Map> map_; + + // Render thread + std::unique_ptr<RendererBackend> rendererBackend_; + std::unique_ptr<mbgl::Renderer> renderer_; + + mbgl::RendererObserver* rendererObserver_ = nullptr; + uint32_t defaultFramebufferID_{0}; + int32_t x_ = 0; + int32_t y_ = 0; + + std::shared_ptr<mapbox::map::MapObserver> observer_; + std::shared_ptr<MapClient> client_; + + std::shared_ptr<Scheduler::NotifyFn> schedulerNotify_{}; +}; + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/renderer_backend.cpp b/src/mapbox/map/renderer_backend.cpp new file mode 100644 index 0000000000..65af09f494 --- /dev/null +++ b/src/mapbox/map/renderer_backend.cpp @@ -0,0 +1,61 @@ +#include <mapbox/map/renderer_backend.hpp> + +#include <mbgl/gfx/backend_scope.hpp> +#include <mbgl/gl/renderable_resource.hpp> + +namespace mapbox { +namespace map { + +class RenderableResource final : public mbgl::gl::RenderableResource { +public: + RenderableResource(RendererBackend& backend_) : backend(backend_) {} + + void bind() override { + backend.bind(); + } + +private: + RendererBackend& backend; +}; + +RendererBackend::RendererBackend(GetGLProcAddressFn&& getProcAddressFn, mbgl::Size fbSize, uint32_t fbo) + : mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Shared) + , mbgl::gfx::Renderable(fbSize, std::make_unique<RenderableResource>(*this)) + , getProcAddressFn_(std::move(getProcAddressFn)), size_(fbSize), fbo_(fbo) {} + +RendererBackend::~RendererBackend() = default; + +void RendererBackend::resize(mbgl::Size fbSize) { + size_ = fbSize; +} + +void RendererBackend::move(int32_t x, int32_t y) { + x_ = x; + y_ = y; +} + +void RendererBackend::updateAssumedState() { + assumeFramebufferBinding(ImplicitFramebufferBinding); + assumeViewport(x_, y_, getFramebufferSize()); +} + +void RendererBackend::bind() { + assert(mbgl::gfx::BackendScope::exists()); + setFramebufferBinding(fbo_); + setViewport(x_, y_, size_); +} + +mbgl::Size RendererBackend::getFramebufferSize() const { + return size_; +} + +mbgl::gl::ProcAddress RendererBackend::getExtensionFunctionPointer(const char* name) { + return getProcAddressFn_(name); +} + +void RendererBackend::activate() {} + +void RendererBackend::deactivate() {} + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/renderer_backend.hpp b/src/mapbox/map/renderer_backend.hpp new file mode 100644 index 0000000000..a305ed401d --- /dev/null +++ b/src/mapbox/map/renderer_backend.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <mapbox/map/map_client.hpp> + +#include <mbgl/gfx/renderable.hpp> +#include <mbgl/gl/renderer_backend.hpp> + +#include <functional> + +namespace mapbox { +namespace map { + +class RendererBackend : public mbgl::gl::RendererBackend, + public mbgl::gfx::Renderable { +public: + using GetGLProcAddressFn = std::function<MapClient::GLProcAddress(const char *)>; + + RendererBackend(GetGLProcAddressFn &&, mbgl::Size, uint32_t fbo); + ~RendererBackend() override; + + void resize(mbgl::Size); + void move(int32_t x, int32_t y); + + void bind(); + mbgl::Size getFramebufferSize() const; + + void setDefaultFramebufferObject(uint32_t framebufferID) { fbo_ = framebufferID; } + + // mbgl::gfx::RendererBackend implementation + mbgl::gfx::Renderable& getDefaultRenderable() override { + return *this; + } + +protected: + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char *) final; + void activate() final; + void deactivate() final; + void updateAssumedState() final; + +private: + GetGLProcAddressFn getProcAddressFn_; + mbgl::Size size_; + int32_t x_ = 0; + int32_t y_ = 0; + uint32_t fbo_; +}; + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/scheduler.cpp b/src/mapbox/map/scheduler.cpp new file mode 100644 index 0000000000..c9ef3ea03f --- /dev/null +++ b/src/mapbox/map/scheduler.cpp @@ -0,0 +1,51 @@ +#include <mapbox/map/scheduler.hpp> + +#include <cassert> + +namespace mapbox { +namespace map { + +Scheduler::Scheduler() { + assert(threadId_ == std::this_thread::get_id()); +} + +Scheduler::~Scheduler() { + assert(threadId_ == std::this_thread::get_id()); +} + +void Scheduler::schedule(std::weak_ptr<mbgl::Mailbox> mailbox) { + std::lock_guard<std::mutex> lock(taskQueueMutex_); + taskQueue_.push(mailbox); + + for (auto it = notifyFns_.begin(); it != notifyFns_.end();) { + auto fn = it->lock(); + if (fn) { + fn->operator()(); + it++; + } else { + it = notifyFns_.erase(it); + } + } +} + +void Scheduler::attach(std::weak_ptr<NotifyFn>&& fn) { + notifyFns_.push_back(fn); +} + +void Scheduler::processEvents() { + assert(threadId_ == std::this_thread::get_id()); + + std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue; + { + std::unique_lock<std::mutex> lock(taskQueueMutex_); + std::swap(taskQueue, taskQueue_); + } + + while (!taskQueue.empty()) { + mbgl::Mailbox::maybeReceive(taskQueue.front()); + taskQueue.pop(); + } +} + +} // namespace map +} // namespace mapbox diff --git a/src/mapbox/map/scheduler.hpp b/src/mapbox/map/scheduler.hpp new file mode 100644 index 0000000000..d3b5daef11 --- /dev/null +++ b/src/mapbox/map/scheduler.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include <functional> +#include <list> +#include <memory> +#include <mutex> +#include <queue> +#include <thread> + +namespace mapbox { +namespace map { + +class Scheduler : public mbgl::Scheduler { +public: + using NotifyFn = std::function<void()>; + + explicit Scheduler(); + ~Scheduler() override; + + // mbgl::Scheduler implementation. + void schedule(std::weak_ptr<mbgl::Mailbox>) final; + void processEvents(); + + void attach(std::weak_ptr<NotifyFn>&&); + +private: + const std::thread::id threadId_ = std::this_thread::get_id(); + + std::list<std::weak_ptr<NotifyFn>> notifyFns_; + + std::mutex taskQueueMutex_; + std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue_; +}; + +} // namespace map +} // namespace mapbox |