diff options
Diffstat (limited to 'platform/android/src')
53 files changed, 1532 insertions, 928 deletions
diff --git a/platform/android/src/android_renderer_backend.cpp b/platform/android/src/android_renderer_backend.cpp new file mode 100755 index 0000000000..9e49915650 --- /dev/null +++ b/platform/android/src/android_renderer_backend.cpp @@ -0,0 +1,63 @@ +#include "android_renderer_backend.hpp" + +#include <mbgl/gl/context.hpp> + +#include <EGL/egl.h> + +#include <cassert> + +namespace mbgl { +namespace android { + +/** + * From mbgl::View + */ +void AndroidRendererBackend::bind() { + assert(BackendScope::exists()); + setFramebufferBinding(0); + setViewport(0, 0, getFramebufferSize()); +} + +/** + * From mbgl::RendererBackend. + */ +gl::ProcAddress AndroidRendererBackend::initializeExtension(const char* name) { + assert(BackendScope::exists()); + return eglGetProcAddress(name); +} + +void AndroidRendererBackend::updateViewPort() { + assert(BackendScope::exists()); + setViewport(0, 0, getFramebufferSize()); +} + +void AndroidRendererBackend::resizeFramebuffer(int width, int height) { + fbWidth = width; + fbHeight = height; +} + +PremultipliedImage AndroidRendererBackend::readFramebuffer() const { + assert(BackendScope::exists()); + return RendererBackend::readFramebuffer(getFramebufferSize()); +} + +mbgl::Size AndroidRendererBackend::getFramebufferSize() const { + return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; +} + +/** + * From mbgl::RendererBackend. + */ +void AndroidRendererBackend::updateAssumedState() { + assumeFramebufferBinding(0); + assumeViewport(0, 0, getFramebufferSize()); +} + +void AndroidRendererBackend::markContextLost() { + if (context) { + context->setCleanupOnDestruction(false); + } +} + +} // namespace android +} // namspace mbgl diff --git a/platform/android/src/android_renderer_backend.hpp b/platform/android/src/android_renderer_backend.hpp new file mode 100755 index 0000000000..c5c552459f --- /dev/null +++ b/platform/android/src/android_renderer_backend.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/renderer/renderer_backend.hpp> + +namespace mbgl { +namespace android { + +class AndroidRendererBackend : public RendererBackend { +public: + + // mbgl::RendererBackend // + void bind() override; + void updateAssumedState() override; + mbgl::Size getFramebufferSize() const override; + + // Ensures the current context is not + // cleaned up when destroyed + void markContextLost(); + + void updateViewPort(); + + void resizeFramebuffer(int width, int height); + PremultipliedImage readFramebuffer() const; + +protected: + // mbgl::RendererBackend // + gl::ProcAddress initializeExtension(const char*) override; + void activate() override {}; + void deactivate() override {}; + + +private: + + // Minimum texture size according to OpenGL ES 2.0 specification. + int fbWidth = 64; + int fbHeight = 64; + +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp new file mode 100644 index 0000000000..b80e23e21f --- /dev/null +++ b/platform/android/src/android_renderer_frontend.cpp @@ -0,0 +1,117 @@ +#include "android_renderer_frontend.hpp" + +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/run_loop.hpp> + +#include "android_renderer_backend.hpp" + +namespace mbgl { +namespace android { + +// Forwards RendererObserver signals to the given +// Delegate RendererObserver on the given RunLoop +class ForwardingRendererObserver : public RendererObserver { +public: + ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_) + : mailbox(std::make_shared<Mailbox>(mapRunLoop)) + , delegate(delegate_, mailbox) { + } + + ~ForwardingRendererObserver() { + mailbox->close(); + } + + void onInvalidate() override { + delegate.invoke(&RendererObserver::onInvalidate); + } + + void onResourceError(std::exception_ptr err) override { + delegate.invoke(&RendererObserver::onResourceError, err); + } + + void onWillStartRenderingMap() override { + delegate.invoke(&RendererObserver::onWillStartRenderingMap); + } + + void onWillStartRenderingFrame() override { + delegate.invoke(&RendererObserver::onWillStartRenderingFrame); + } + + void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override { + delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded); + } + + void onDidFinishRenderingMap() override { + delegate.invoke(&RendererObserver::onDidFinishRenderingMap); + } + +private: + std::shared_ptr<Mailbox> mailbox; + ActorRef<RendererObserver> delegate; +}; + +AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_) + : mapRenderer(mapRenderer_) + , mapRunLoop(util::RunLoop::Get()) { +} + +AndroidRendererFrontend::~AndroidRendererFrontend() = default; + +void AndroidRendererFrontend::reset() { + mapRenderer.reset(); +} + +void AndroidRendererFrontend::setObserver(RendererObserver& observer) { + assert (util::RunLoop::Get()); + // Don't call the Renderer directly, but use MapRenderer#setObserver to make sure + // the Renderer may be re-initialised without losing the RendererObserver reference. + mapRenderer.setObserver(std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer)); +} + +void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) { + mapRenderer.update(std::move(params)); + mapRenderer.requestRender(); +} + +void AndroidRendererFrontend::onLowMemory() { + mapRenderer.actor().invoke(&Renderer::onLowMemory); +} + +std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID, + const SourceQueryOptions& options) const { + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(&Renderer::querySourceFeatures, sourceID, options).get(); +} + +std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures; + + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(fn, box, options).get(); +} + +std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures; + + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(fn, point, options).get(); +} + +AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const { + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get(); +} + +} // namespace android +} // namespace mbgl + diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp new file mode 100644 index 0000000000..94508fd816 --- /dev/null +++ b/platform/android/src/android_renderer_frontend.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <mbgl/actor/actor.hpp> +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/renderer/renderer_frontend.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <functional> +#include <memory> +#include <vector> +#include <string> + +#include "map_renderer.hpp" + +namespace mbgl { + +class RenderedQueryOptions; +class SourceQueryOptions; + +namespace android { + +class AndroidRendererFrontend : public RendererFrontend { +public: + + AndroidRendererFrontend(MapRenderer&); + ~AndroidRendererFrontend() override; + + void reset() override; + void setObserver(RendererObserver&) override; + + void update(std::shared_ptr<UpdateParameters>) override; + + // Feature querying + std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const; + std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const; + std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; + AnnotationIDs queryPointAnnotations(const ScreenBox& box) const; + + // Memory + void onLowMemory(); + +private: + MapRenderer& mapRenderer; + util::RunLoop* mapRunLoop; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/asset_manager_file_source.cpp b/platform/android/src/asset_manager_file_source.cpp index 6a3113d696..aa65e3ff48 100644 --- a/platform/android/src/asset_manager_file_source.cpp +++ b/platform/android/src/asset_manager_file_source.cpp @@ -1,5 +1,6 @@ #include "asset_manager_file_source.hpp" +#include <mbgl/storage/file_source_request.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/util.hpp> #include <mbgl/util/thread.hpp> @@ -12,10 +13,10 @@ namespace mbgl { class AssetManagerFileSource::Impl { public: - Impl(AAssetManager* assetManager_) : assetManager(assetManager_) { + Impl(ActorRef<Impl>, AAssetManager* assetManager_) : assetManager(assetManager_) { } - void request(const std::string& url, FileSource::Callback callback) { + void request(const std::string& url, ActorRef<FileSourceRequest> req) { // Note: AssetManager already prepends "assets" to the filename. const std::string path = mbgl::util::percentDecode(url.substr(8)); @@ -30,7 +31,7 @@ public: "Could not read asset"); } - callback(response); + req.invoke(&FileSourceRequest::setResponse, response); } private: @@ -39,15 +40,18 @@ private: AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env, jni::Object<android::AssetManager> assetManager_) : assetManager(assetManager_.NewGlobalRef(env)), - thread(std::make_unique<util::Thread<Impl>>( - util::ThreadContext{"AssetManagerFileSource", util::ThreadPriority::Low}, - AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) { + impl(std::make_unique<util::Thread<Impl>>("AssetManagerFileSource", + AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) { } AssetManagerFileSource::~AssetManagerFileSource() = default; std::unique_ptr<AsyncRequest> AssetManagerFileSource::request(const Resource& resource, Callback callback) { - return thread->invokeWithCallback(&Impl::request, resource.url, callback); + auto req = std::make_unique<FileSourceRequest>(std::move(callback)); + + impl->actor().invoke(&Impl::request, resource.url, req->actor()); + + return std::move(req); } } // namespace mbgl diff --git a/platform/android/src/asset_manager_file_source.hpp b/platform/android/src/asset_manager_file_source.hpp index 7a447a2c61..dcad95664a 100644 --- a/platform/android/src/asset_manager_file_source.hpp +++ b/platform/android/src/asset_manager_file_source.hpp @@ -20,9 +20,10 @@ public: std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; private: - jni::UniqueObject<android::AssetManager> assetManager; class Impl; - std::unique_ptr<util::Thread<Impl>> thread; + + jni::UniqueObject<android::AssetManager> assetManager; + std::unique_ptr<util::Thread<Impl>> impl; }; } // namespace mbgl diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp index 2a0b710f73..f1c72eb5dd 100644 --- a/platform/android/src/conversion/constant.hpp +++ b/platform/android/src/conversion/constant.hpp @@ -3,6 +3,7 @@ #include "conversion.hpp" #include <mbgl/util/optional.hpp> +#include <mbgl/util/color.hpp> #include <jni/jni.hpp> #include <string> diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index c55c9c3527..1ed68d0835 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -92,6 +92,10 @@ void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& reinterpret_cast<ExampleCustomLayer*>(context)->render(); } +void nativeContextLost(void */*context*/) { + mbgl::Log::Info(mbgl::Event::General, "nativeContextLost"); +} + void nativeDenitialize(void *context) { mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize"); delete reinterpret_cast<ExampleCustomLayer*>(context); @@ -123,6 +127,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { reinterpret_cast<jlong>(nativeRender)); env->SetStaticLongField(customLayerClass, + env->GetStaticFieldID(customLayerClass, "ContextLostFunction", "J"), + reinterpret_cast<jlong>(nativeContextLost)); + + env->SetStaticLongField(customLayerClass, env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"), reinterpret_cast<jlong>(nativeDenitialize)); diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 16c09b7b52..262e3d3c6a 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -1,5 +1,8 @@ #include "file_source.hpp" +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/storage/resource_transform.hpp> #include <mbgl/util/logging.hpp> #include "asset_manager_file_source.hpp" @@ -42,21 +45,22 @@ void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) { void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) { if (transformCallback) { - // Launch transformCallback - fileSource->setResourceTransform([ + resourceTransform = std::make_unique<Actor<ResourceTransform>>(*Scheduler::GetCurrent(), // Capture the ResourceTransformCallback object as a managed global into // the lambda. It is released automatically when we're setting a new ResourceTransform in // a subsequent call. // Note: we're converting it to shared_ptr because this lambda is converted to a std::function, // which requires copyability of its captured variables. - callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter()) - ](mbgl::Resource::Kind kind, std::string&& url_) { - android::UniqueEnv _env = android::AttachEnv(); - return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_); - }); + [callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter())] + (mbgl::Resource::Kind kind, const std::string&& url_) { + android::UniqueEnv _env = android::AttachEnv(); + return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_); + }); + fileSource->setResourceTransform(resourceTransform->self()); } else { // Reset the callback - fileSource->setResourceTransform(nullptr); + resourceTransform.reset(); + fileSource->setResourceTransform({}); } } @@ -106,4 +110,4 @@ std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni:: } } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp index 55e70f34d9..4abe352bff 100644 --- a/platform/android/src/file_source.hpp +++ b/platform/android/src/file_source.hpp @@ -7,6 +7,10 @@ #include <jni/jni.hpp> namespace mbgl { + +template <typename T> class Actor; +class ResourceTransform; + namespace android { /** @@ -46,10 +50,10 @@ public: static void registerNative(jni::JNIEnv&); private: - + std::unique_ptr<Actor<ResourceTransform>> resourceTransform; std::unique_ptr<mbgl::DefaultFileSource> fileSource; }; } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/geometry/lat_lng_quad.cpp b/platform/android/src/geometry/lat_lng_quad.cpp new file mode 100644 index 0000000000..2b36139e18 --- /dev/null +++ b/platform/android/src/geometry/lat_lng_quad.cpp @@ -0,0 +1,39 @@ +#include "lat_lng_quad.hpp" +#include "lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Object<LatLngQuad> LatLngQuad::New(jni::JNIEnv& env, std::array<mbgl::LatLng, 4> coordinates) { + static auto quadConstructor = LatLngQuad::javaClass.GetConstructor<jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>>(env); + return LatLngQuad::javaClass.New(env, quadConstructor, + LatLng::New(env, coordinates[0]), + LatLng::New(env, coordinates[1]), + LatLng::New(env, coordinates[2]), + LatLng::New(env, coordinates[3])); +} + +std::array<mbgl::LatLng, 4> LatLngQuad::getLatLngArray(jni::JNIEnv& env, jni::Object<LatLngQuad> quad) { + static auto topLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topLeft"); + static auto topRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topRight"); + static auto bottomRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomRight"); + static auto bottomLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomLeft"); + + return std::array < mbgl::LatLng, 4 > {{ + LatLng::getLatLng(env, quad.Get(env, topLeftField)), + LatLng::getLatLng(env, quad.Get(env, topRightField)), + LatLng::getLatLng(env, quad.Get(env, bottomRightField)), + LatLng::getLatLng(env, quad.Get(env, bottomLeftField)) + }}; +} + +void LatLngQuad::registerNative(jni::JNIEnv& env) { + // Lookup the class + LatLngQuad::javaClass = *jni::Class<LatLngQuad>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<LatLngQuad> LatLngQuad::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng_quad.hpp b/platform/android/src/geometry/lat_lng_quad.hpp new file mode 100644 index 0000000000..8f8c9abeef --- /dev/null +++ b/platform/android/src/geometry/lat_lng_quad.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> +#include <array> + +namespace mbgl { +namespace android { + +class LatLngQuad : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngQuad"; }; + + static jni::Object<LatLngQuad> New(jni::JNIEnv&, std::array<mbgl::LatLng, 4>); + + static std::array<mbgl::LatLng, 4> getLatLngArray(jni::JNIEnv&, jni::Object<LatLngQuad>); + + static jni::Class<LatLngQuad> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index ee1429bd74..8eb9416e9d 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -117,7 +117,9 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, } if (cacheControl) { - response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint(); + const auto cc = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()); + response.expires = cc.toTimePoint(); + response.mustRevalidate = cc.mustRevalidate; } if (expires) { diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 6c490fad5c..6acb6a3664 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -23,6 +23,7 @@ #include "geojson/position.hpp" #include "geometry/lat_lng.hpp" #include "geometry/lat_lng_bounds.hpp" +#include "geometry/lat_lng_quad.hpp" #include "geometry/projected_meters.hpp" #include "graphics/pointf.hpp" #include "graphics/rectf.hpp" @@ -31,6 +32,8 @@ #include "gson/json_object.hpp" #include "gson/json_primitive.hpp" #include "java_types.hpp" +#include "map_renderer.hpp" +#include "map_renderer_runnable.hpp" #include "native_map_view.hpp" #include "offline/offline_manager.hpp" #include "offline/offline_region.hpp" @@ -46,6 +49,7 @@ #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" #include "style/light.hpp" +#include "snapshotter/map_snapshotter.hpp" namespace mbgl { namespace android { @@ -127,6 +131,7 @@ void registerNatives(JavaVM *vm) { // Geometry LatLng::registerNative(env); LatLngBounds::registerNative(env); + LatLngQuad::registerNative(env); ProjectedMeters::registerNative(env); // GSon @@ -141,6 +146,8 @@ void registerNatives(JavaVM *vm) { Polyline::registerNative(env); // Map + MapRenderer::registerNative(env); + MapRendererRunnable::registerNative(env); NativeMapView::registerNative(env); // Http @@ -175,6 +182,9 @@ void registerNatives(JavaVM *vm) { OfflineTilePyramidRegionDefinition::registerNative(env); OfflineRegionError::registerNative(env); OfflineRegionStatus::registerNative(env); + + // Snapshotter + MapSnapshotter::registerNative(env); } } // namespace android diff --git a/platform/android/src/jni/generic_global_ref_deleter.hpp b/platform/android/src/jni/generic_global_ref_deleter.hpp index 4e53e0a0ce..7239e361a7 100644 --- a/platform/android/src/jni/generic_global_ref_deleter.hpp +++ b/platform/android/src/jni/generic_global_ref_deleter.hpp @@ -18,5 +18,34 @@ struct GenericGlobalRefDeleter { } }; + +template < class TagType > +class GenericWeakObjectRefDeleter; + +template < class TagType = jni::ObjectTag > +using GenericUniqueWeakObject = std::unique_ptr< const jni::Object<TagType>, GenericWeakObjectRefDeleter<TagType> >; + +template < class TagType > +class GenericWeakObjectRefDeleter +{ +public: + using pointer = jni::PointerToValue< jni::Object<TagType> >; + + void operator()(pointer p) const + { + if (p) + { + auto env = AttachEnv(); + env->DeleteWeakGlobalRef(jni::Unwrap(p->Get())); + } + } +}; + +template < class TagType > +GenericUniqueWeakObject<TagType> SeizeGenericWeakRef(JNIEnv&, jni::Object<TagType>&& object) +{ + return GenericUniqueWeakObject<TagType>(jni::PointerToValue<jni::Object<TagType>>(std::move(object)), GenericWeakObjectRefDeleter<TagType>()); +}; + } // namespace android } // namespace mbgl diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index d6f2cb83e8..1fc5f9789f 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -27,6 +27,24 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp return CameraPosition::javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees); } +mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Object<CameraPosition> position) { + static auto bearing = CameraPosition::javaClass.GetField<jni::jdouble>(env, "bearing"); + static auto target = CameraPosition::javaClass.GetField<jni::Object<LatLng>>(env, "target"); + static auto tilt = CameraPosition::javaClass.GetField<jni::jdouble>(env, "tilt"); + static auto zoom = CameraPosition::javaClass.GetField<jni::jdouble>(env, "zoom"); + + auto center = LatLng::getLatLng(env, position.Get(env, target)); + + return mbgl::CameraOptions { + center, + {}, + {}, + position.Get(env, zoom), + position.Get(env, bearing) * util::DEG2RAD, + position.Get(env, tilt) + }; +} + void CameraPosition::registerNative(jni::JNIEnv &env) { // Lookup the class CameraPosition::javaClass = *jni::Class<CameraPosition>::Find(env).NewGlobalRef(env).release(); diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp index b9f1646cc9..4eee8be758 100644 --- a/platform/android/src/map/camera_position.hpp +++ b/platform/android/src/map/camera_position.hpp @@ -15,6 +15,8 @@ public: static jni::Object<CameraPosition> New(jni::JNIEnv&, mbgl::CameraOptions); + static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, jni::Object<CameraPosition>); + static jni::Class<CameraPosition> javaClass; static void registerNative(jni::JNIEnv&); diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp new file mode 100644 index 0000000000..7655455210 --- /dev/null +++ b/platform/android/src/map_renderer.cpp @@ -0,0 +1,200 @@ +#include "map_renderer.hpp" + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <string> + +#include "attach_env.hpp" +#include "android_renderer_backend.hpp" +#include "map_renderer_runnable.hpp" +#include "file_source.hpp" + +namespace mbgl { +namespace android { + +MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object<MapRenderer> obj, + jni::Object<FileSource> _fileSource, jni::jfloat pixelRatio_, + jni::String programCacheDir_) + : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapRenderer>(jni::NewWeakGlobalRef(_env, obj.Get()).release()))), pixelRatio(pixelRatio_) + , fileSource(FileSource::getDefaultFileSource(_env, _fileSource)) + , programCacheDir(jni::Make<std::string>(_env, programCacheDir_)) + , threadPool(sharedThreadPool()) + , mailbox(std::make_shared<Mailbox>(*this)) { +} + +MapRenderer::~MapRenderer() = default; + +void MapRenderer::reset() { + // Make sure to destroy the renderer on the GL Thread + auto self = ActorRef<MapRenderer>(*this, mailbox); + self.ask(&MapRenderer::resetRenderer).wait(); + + // Lock to make sure there is no concurrent initialisation on the gl thread + std::lock_guard<std::mutex> lock(initialisationMutex); + rendererObserver.reset(); +} + +ActorRef<Renderer> MapRenderer::actor() const { + return *rendererRef; +} + +void MapRenderer::schedule(std::weak_ptr<Mailbox> scheduled) { + // Create a runnable and schedule it on the gl thread + android::UniqueEnv _env = android::AttachEnv(); + auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled)); + + static auto queueEvent = javaClass.GetMethod<void( + jni::Object<MapRendererRunnable>)>(*_env, "queueEvent"); + javaPeer->Call(*_env, queueEvent, runnable->getPeer()); + + // Release the object as it will be destroyed on GC of the Java Peer + runnable.release(); +} + +void MapRenderer::requestRender() { + android::UniqueEnv _env = android::AttachEnv(); + static auto onInvalidate = javaClass.GetMethod<void()>(*_env, "requestRender"); + javaPeer->Call(*_env, onInvalidate); +} + +void MapRenderer::update(std::shared_ptr<UpdateParameters> params) { + // Lock on the parameters + std::lock_guard<std::mutex> lock(updateMutex); + updateParameters = std::move(params); +} + +void MapRenderer::setObserver(std::shared_ptr<RendererObserver> _rendererObserver) { + // Lock as the initialization can come from the main thread or the GL thread first + std::lock_guard<std::mutex> lock(initialisationMutex); + + rendererObserver = std::move(_rendererObserver); + + // Set the new observer on the Renderer implementation + if (renderer) { + renderer->setObserver(rendererObserver.get()); + } +} + +void MapRenderer::requestSnapshot(SnapshotCallback callback) { + auto self = ActorRef<MapRenderer>(*this, mailbox); + self.invoke( + &MapRenderer::scheduleSnapshot, + std::make_unique<SnapshotCallback>([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { + runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { + callback(std::move(image)); + }); + snapshotCallback.reset(); + }) + ); +} + +// Called on OpenGL thread // + +void MapRenderer::resetRenderer() { + assert (renderer); + renderer.reset(); +} + +void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) { + snapshotCallback = std::move(callback); + requestRender(); +} + +void MapRenderer::render(JNIEnv&) { + assert (renderer); + + std::shared_ptr<UpdateParameters> params; + { + // Lock on the parameters + std::unique_lock<std::mutex> lock(updateMutex); + if (!updateParameters) return; + + // Hold on to the update parameters during render + params = updateParameters; + } + + // Activate the backend + BackendScope backendGuard { *backend }; + + // Ensure that the "current" scheduler on the render thread is + // this scheduler. + Scheduler::SetCurrent(this); + + if (framebufferSizeChanged) { + backend->updateViewPort(); + framebufferSizeChanged = false; + } + + renderer->render(*params); + + // Deliver the snapshot if requested + if (snapshotCallback) { + snapshotCallback->operator()(backend->readFramebuffer()); + snapshotCallback.reset(); + } +} + +void MapRenderer::onSurfaceCreated(JNIEnv&) { + // Lock as the initialization can come from the main thread or the GL thread first + std::lock_guard<std::mutex> lock(initialisationMutex); + + // The android system will have already destroyed the underlying + // GL resources if this is not the first intialization and an + // attempt to clean them up will fail + if (backend) backend->markContextLost(); + if (renderer) renderer->markContextLost(); + + // Reset in opposite order + renderer.reset(); + backend.reset(); + + // Create the new backend and renderer + backend = std::make_unique<AndroidRendererBackend>(); + renderer = std::make_unique<Renderer>(*backend, pixelRatio, fileSource, *threadPool, + GLContextMode::Unique, programCacheDir); + rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailbox); + + // Set the observer on the new Renderer implementation + if (rendererObserver) { + renderer->setObserver(rendererObserver.get()); + } +} + +void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { + backend->resizeFramebuffer(width, height); + framebufferSizeChanged = true; + requestRender(); +} + +// Static methods // + +jni::Class<MapRenderer> MapRenderer::javaClass; + +void MapRenderer::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapRenderer::javaClass = *jni::Class<MapRenderer>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<MapRenderer>(env, MapRenderer::javaClass, "nativePtr", + std::make_unique<MapRenderer, JNIEnv&, jni::Object<MapRenderer>, jni::Object<FileSource>, jni::jfloat, jni::String>, + "nativeInitialize", "finalize", + METHOD(&MapRenderer::render, "nativeRender"), + METHOD(&MapRenderer::onSurfaceCreated, + "nativeOnSurfaceCreated"), + METHOD(&MapRenderer::onSurfaceChanged, + "nativeOnSurfaceChanged")); +} + +MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object<MapRenderer> jObject) { + static auto field = MapRenderer::javaClass.GetField<jlong>(env, "nativePtr"); + MapRenderer* mapRenderer = reinterpret_cast<MapRenderer*>(jObject.Get(env, field)); + assert(mapRenderer != nullptr); + return *mapRenderer; +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp new file mode 100644 index 0000000000..0d614912a9 --- /dev/null +++ b/platform/android/src/map_renderer.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/image.hpp> + +#include <memory> +#include <utility> + +#include <jni/jni.hpp> +#include <mbgl/storage/default_file_source.hpp> + +#include "jni/generic_global_ref_deleter.hpp" + +namespace mbgl { + +template <class> +class ActorRef; +class Mailbox; +class Renderer; +class RendererBackend; +class RendererObserver; +class ThreadPool; +class UpdateParameters; + +namespace android { + +class AndroidRendererBackend; +class FileSource; + +/** + * The MapRenderer is a peer class that encapsulates the actions + * performed on the GL Thread. + * + * The public methods are safe to call from the main thread, others are not. + */ +class MapRenderer : public Scheduler { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRenderer"; }; + + static jni::Class<MapRenderer> javaClass; + + static void registerNative(jni::JNIEnv&); + + static MapRenderer& getNativePeer(JNIEnv&, jni::Object<MapRenderer>); + + MapRenderer(jni::JNIEnv& _env, + jni::Object<MapRenderer>, + jni::Object<FileSource>, + jni::jfloat pixelRatio, + jni::String programCacheDir); + + ~MapRenderer() override; + + // Resets the renderer to clean up on the calling thread + void reset(); + + // Takes the RendererObserver by shared_ptr so we + // don't have to make the header public. Use + // this instead of Renderer#setObserver directly + void setObserver(std::shared_ptr<RendererObserver>); + + // Sets the new update parameters to use on subsequent + // renders. Be sure to trigger a render with + // requestRender(). + void update(std::shared_ptr<UpdateParameters>); + + // Gives a handle to the Renderer to enable actions on + // any thread. + ActorRef<Renderer> actor() const; + + // From Scheduler. Schedules by using callbacks to the + // JVM to process the mailbox on the right thread. + void schedule(std::weak_ptr<Mailbox> scheduled) override; + + void requestRender(); + + // Snapshot - requires a RunLoop on the calling thread + using SnapshotCallback = std::function<void (PremultipliedImage)>; + void requestSnapshot(SnapshotCallback); + +protected: + // Called from the GL Thread // + + void scheduleSnapshot(std::unique_ptr<SnapshotCallback>); + +private: + // Called from the GL Thread // + + // Resets the renderer + void resetRenderer(); + + // Renders a frame. + void render(JNIEnv&); + + void onSurfaceCreated(JNIEnv&); + + void onSurfaceChanged(JNIEnv&, jint width, jint height); + +private: + GenericUniqueWeakObject<MapRenderer> javaPeer; + + float pixelRatio; + DefaultFileSource& fileSource; + std::string programCacheDir; + + std::shared_ptr<ThreadPool> threadPool; + std::shared_ptr<Mailbox> mailbox; + + std::mutex initialisationMutex; + std::shared_ptr<RendererObserver> rendererObserver; + + std::unique_ptr<AndroidRendererBackend> backend; + std::unique_ptr<Renderer> renderer; + std::unique_ptr<ActorRef<Renderer>> rendererRef; + + std::shared_ptr<UpdateParameters> updateParameters; + std::mutex updateMutex; + + bool framebufferSizeChanged = false; + + std::unique_ptr<SnapshotCallback> snapshotCallback; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer_runnable.cpp b/platform/android/src/map_renderer_runnable.cpp new file mode 100644 index 0000000000..df8cba5e55 --- /dev/null +++ b/platform/android/src/map_renderer_runnable.cpp @@ -0,0 +1,49 @@ +#include "map_renderer_runnable.hpp" + +#include <mbgl/util/logging.hpp> + +namespace mbgl { +namespace android { + +MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::weak_ptr<Mailbox> mailbox_) + : mailbox(std::move(mailbox_)) { + + // Create the Java peer + jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 5); + static auto constructor = javaClass.GetConstructor<jlong>(env); + auto instance = javaClass.New(env, constructor, reinterpret_cast<jlong>(this)); + javaPeer = SeizeGenericWeakRef(env, jni::Object<MapRendererRunnable>(jni::NewWeakGlobalRef(env, instance.Get()).release())); +} + +MapRendererRunnable::~MapRendererRunnable() = default; + +void MapRendererRunnable::run(jni::JNIEnv&) { + Mailbox::maybeReceive(mailbox); +} + +jni::Object<MapRendererRunnable> MapRendererRunnable::getPeer() { + return *javaPeer; +} + +// Static methods // + +jni::Class<MapRendererRunnable> MapRendererRunnable::javaClass; + +void MapRendererRunnable::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapRendererRunnable::javaClass = *jni::Class<MapRendererRunnable>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + jni::RegisterNativePeer<MapRendererRunnable>( + env, + MapRendererRunnable::javaClass, + "nativePtr", + std::make_unique<MapRendererRunnable, JNIEnv&>, + "nativeInitialize", + "finalize", + METHOD(&MapRendererRunnable::run, "run")); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer_runnable.hpp b/platform/android/src/map_renderer_runnable.hpp new file mode 100644 index 0000000000..75646a442d --- /dev/null +++ b/platform/android/src/map_renderer_runnable.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include <memory> +#include <utility> + +#include <jni/jni.hpp> + +#include "jni/generic_global_ref_deleter.hpp" + +namespace mbgl { +namespace android { + +/** + * The MapRendererRunnable is a peer class that encapsulates + * a scheduled mailbox in a Java Runnable so it can be + * scheduled on the map renderer thread. + * + */ +class MapRendererRunnable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable"; }; + + static jni::Class<MapRendererRunnable> javaClass; + + static void registerNative(jni::JNIEnv&); + + MapRendererRunnable(jni::JNIEnv&, std::weak_ptr<Mailbox>); + + // Only for jni registration, unused + MapRendererRunnable(jni::JNIEnv&) { + assert(false); + } + + ~MapRendererRunnable(); + + void run(jni::JNIEnv&); + + jni::Object<MapRendererRunnable> getPeer(); + +private: + GenericUniqueWeakObject<MapRendererRunnable> javaPeer; + std::weak_ptr<Mailbox> mailbox; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 159ba70508..a9ed6d5ead 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -9,12 +9,10 @@ #include <sys/system_properties.h> -#include <EGL/egl.h> #include <android/native_window_jni.h> #include <jni/jni.hpp> -#include <mbgl/map/backend_scope.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/event.hpp> @@ -25,8 +23,10 @@ #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/projection.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/filter.hpp> +#include <mbgl/renderer/query.hpp> // Java -> C++ conversion #include "style/android_conversion.hpp" @@ -41,12 +41,16 @@ #include "jni.hpp" #include "attach_env.hpp" +#include "map_renderer.hpp" +#include "android_renderer_frontend.hpp" +#include "file_source.hpp" #include "bitmap.hpp" #include "run_loop_impl.hpp" #include "java/util.hpp" #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" #include "style/light.hpp" +#include "bitmap_factory.hpp" namespace mbgl { namespace android { @@ -54,15 +58,12 @@ namespace android { NativeMapView::NativeMapView(jni::JNIEnv& _env, jni::Object<NativeMapView> _obj, jni::Object<FileSource> jFileSource, - jni::jfloat _pixelRatio, - jni::String _programCacheDir, - jni::jint _availableProcessors, - jni::jlong _totalMemory) - : javaPeer(_obj.NewWeakGlobalRef(_env)), - pixelRatio(_pixelRatio), - availableProcessors(_availableProcessors), - totalMemory(_totalMemory), - threadPool(sharedThreadPool()) { + jni::Object<MapRenderer> jMapRenderer, + jni::jfloat _pixelRatio) + : javaPeer(_obj.NewWeakGlobalRef(_env)) + , mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer)) + , pixelRatio(_pixelRatio) + , threadPool(sharedThreadPool()) { // Get a reference to the JavaVM for callbacks if (_env.GetJavaVM(&vm) < 0) { @@ -70,116 +71,30 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env, return; } - // Create the core map - map = std::make_unique<mbgl::Map>( - *this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }, - pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource), *threadPool, - MapMode::Continuous, GLContextMode::Unique, ConstrainMode::HeightOnly, - ViewportMode::Default, jni::Make<std::string>(_env, _programCacheDir)); - - recalculateSourceTileCacheSize(); -} + // Get native peer for file source + mbgl::FileSource& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource); -void NativeMapView::recalculateSourceTileCacheSize() { - //Calculate a fitting cache size based on device parameters - float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1; - float cpuFactor = availableProcessors; - float memoryFactor = static_cast<float>(totalMemory) / 1000.0f / 1000.0f / 1000.0f; - float sizeFactor = (static_cast<float>(map->getSize().width) / mbgl::util::tileSize) * - (static_cast<float>(map->getSize().height) / mbgl::util::tileSize); + // Create a renderer frontend + rendererFrontend = std::make_unique<AndroidRendererFrontend>(mapRenderer); - map->setSourceTileCacheSize(zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f); + // Create the core map + map = std::make_unique<mbgl::Map>(*rendererFrontend, *this, + mbgl::Size{ static_cast<uint32_t>(width), + static_cast<uint32_t>(height) }, pixelRatio, + fileSource, *threadPool, MapMode::Continuous, + ConstrainMode::HeightOnly, ViewportMode::Default); } /** * Called through NativeMapView#destroy() */ NativeMapView::~NativeMapView() { - _terminateContext(); - _destroySurface(); - _terminateDisplay(); - map.reset(); - vm = nullptr; } /** - * From mbgl::View - */ -void NativeMapView::bind() { - setFramebufferBinding(0); - setViewportSize(getFramebufferSize()); -} - -/** - * From mbgl::Backend. - */ -gl::ProcAddress NativeMapView::initializeExtension(const char* name) { - return eglGetProcAddress(name); -} - -void NativeMapView::activate() { - - oldDisplay = eglGetCurrentDisplay(); - oldReadSurface = eglGetCurrentSurface(EGL_READ); - oldDrawSurface = eglGetCurrentSurface(EGL_DRAW); - oldContext = eglGetCurrentContext(); - - assert(vm != nullptr); - - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglSwapInterval(display, 0)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError()); - throw std::runtime_error("eglSwapInterval() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready"); - } -} - -/** - * From mbgl::Backend. - */ -void NativeMapView::deactivate() { - assert(vm != nullptr); - - if (oldContext != context && oldContext != EGL_NO_CONTEXT) { - if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else if (display != EGL_NO_DISPLAY) { - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready"); - } -} - -/** - * From mbgl::Backend. Callback to java NativeMapView#onInvalidate(). - * - * May be called from any thread - */ -void NativeMapView::invalidate() { - android::UniqueEnv _env = android::AttachEnv(); - static auto onInvalidate = javaClass.GetMethod<void ()>(*_env, "onInvalidate"); - javaPeer->Call(*_env, onInvalidate); -} - -/** - * From mbgl::Backend. Callback to java NativeMapView#onMapChanged(int). + * From mbgl::RendererBackend. Callback to java NativeMapView#onMapChanged(int). * * May be called from any thread */ @@ -257,98 +172,26 @@ void NativeMapView::onSourceChanged(mbgl::style::Source&) { // JNI Methods // -void NativeMapView::initializeDisplay(jni::JNIEnv&) { - _initializeDisplay(); -} - -void NativeMapView::terminateDisplay(jni::JNIEnv&) { - _terminateDisplay(); -} - -void NativeMapView::initializeContext(jni::JNIEnv&) { - _initializeContext(); -} - -void NativeMapView::terminateContext(jni::JNIEnv&) { - _terminateContext(); -} - -void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) { - _createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface))); -} - -void NativeMapView::destroySurface(jni::JNIEnv&) { - _destroySurface(); -} - -void NativeMapView::render(jni::JNIEnv& env) { - BackendScope guard(*this); - - if (framebufferSizeChanged) { - setViewportSize(getFramebufferSize()); - framebufferSizeChanged = false; - } - - map->render(*this); - - if(snapshot){ - snapshot = false; - - // take snapshot - auto image = readFramebuffer(getFramebufferSize()); - auto bitmap = Bitmap::CreateBitmap(env, std::move(image)); - - // invoke Mapview#OnSnapshotReady - android::UniqueEnv _env = android::AttachEnv(); - static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); - javaPeer->Call(*_env, onSnapshotReady, bitmap); - } - - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { - if (!eglSwapBuffers(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", - eglGetError()); - throw std::runtime_error("eglSwapBuffers() failed"); - } - - updateFps(); - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); - } -} - -void NativeMapView::update(jni::JNIEnv&) { - invalidate(); -} - void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) { width = util::max(64, w); height = util::max(64, h); map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); - recalculateSourceTileCacheSize(); -} - -void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) { - fbWidth = w; - fbHeight = h; - framebufferSizeChanged = true; - invalidate(); } jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) { - return jni::Make<jni::String>(env, map->getStyleURL()); + return jni::Make<jni::String>(env, map->getStyle().getURL()); } void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) { - map->setStyleURL(jni::Make<std::string>(env, url)); + map->getStyle().loadURL(jni::Make<std::string>(env, url)); } jni::String NativeMapView::getStyleJson(jni::JNIEnv& env) { - return jni::Make<jni::String>(env, map->getStyleJSON()); + return jni::Make<jni::String>(env, map->getStyle().getJSON()); } void NativeMapView::setStyleJson(jni::JNIEnv& env, jni::String json) { - map->setStyleJSON(jni::Make<std::string>(env, json)); + map->getStyle().loadJSON(jni::Make<std::string>(env, json)); } void NativeMapView::setLatLngBounds(jni::JNIEnv& env, jni::Object<mbgl::android::LatLngBounds> jBounds) { @@ -480,7 +323,6 @@ void NativeMapView::resetZoom(jni::JNIEnv&) { void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) { map->setMinZoom(zoom); - recalculateSourceTileCacheSize(); } jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { @@ -489,7 +331,6 @@ jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) { map->setMaxZoom(zoom); - recalculateSourceTileCacheSize(); } jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) { @@ -554,11 +395,15 @@ void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double b } void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { - snapshot = true; -} + mapRenderer.requestSnapshot([&](PremultipliedImage image) { + auto _env = android::AttachEnv(); + // Convert image to bitmap + auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image)); -void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) { - fpsEnabled = enable; + // invoke Mapview#OnSnapshotReady + static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); + javaPeer->Call(*_env, onSnapshotReady, bitmap); + }); } jni::Object<CameraPosition> NativeMapView::getCameraPosition(jni::JNIEnv& env) { @@ -599,7 +444,7 @@ jni::Array<jni::jlong> NativeMapView::addMarkers(jni::JNIEnv& env, jni::Array<jn } void NativeMapView::onLowMemory(JNIEnv&) { - map->onLowMemory(); + rendererFrontend->onLowMemory(); } using DebugOptions = mbgl::MapDebugOptions; @@ -608,12 +453,10 @@ void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) { DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision : DebugOptions::NoDebug; map->setDebug(debugOptions); - fpsEnabled = debug; } void NativeMapView::cycleDebugOptions(JNIEnv&) { map->cycleDebugOptions(); - fpsEnabled = map->getDebug() != DebugOptions::NoDebug; } jni::jboolean NativeMapView::getDebug(JNIEnv&) { @@ -729,8 +572,13 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, j } jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - map->addAnnotationImage(symbolName, - std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale))); + map->addAnnotationImage(std::make_unique<mbgl::style::Image>( + symbolName, std::move(premultipliedImage), float(scale))); +} + +void NativeMapView::removeAnnotationIcon(JNIEnv& env, jni::String symbol) { + const std::string symbolName = jni::Make<std::string>(env, symbol); + map->removeAnnotationImage(symbolName); } jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::String symbolName) { @@ -738,25 +586,25 @@ jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::S } jlong NativeMapView::getTransitionDuration(JNIEnv&) { - const auto transitionOptions = map->getTransitionOptions(); + const auto transitionOptions = map->getStyle().getTransitionOptions(); return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.duration.value_or(mbgl::Duration::zero())).count(); } void NativeMapView::setTransitionDuration(JNIEnv&, jlong duration) { - auto transitionOptions = map->getTransitionOptions(); + auto transitionOptions = map->getStyle().getTransitionOptions(); transitionOptions.duration.emplace(mbgl::Milliseconds(duration)); - map->setTransitionOptions(transitionOptions); + map->getStyle().setTransitionOptions(transitionOptions); } jlong NativeMapView::getTransitionDelay(JNIEnv&) { - const auto transitionOptions = map->getTransitionOptions(); + const auto transitionOptions = map->getStyle().getTransitionOptions(); return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.delay.value_or(mbgl::Duration::zero())).count(); } void NativeMapView::setTransitionDelay(JNIEnv&, jlong delay) { - auto transitionOptions = map->getTransitionOptions(); + auto transitionOptions = map->getStyle().getTransitionOptions(); transitionOptions.delay.emplace(mbgl::Milliseconds(delay)); - map->setTransitionOptions(transitionOptions); + map->getStyle().setTransitionOptions(transitionOptions); } jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<RectF> rect) { @@ -770,7 +618,7 @@ jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object< }; // Assume only points for now - mbgl::AnnotationIDs ids = map->queryPointAnnotations(box); + mbgl::AnnotationIDs ids = rendererFrontend->queryPointAnnotations(box); // Convert result std::vector<jlong> longIds(ids.begin(), ids.end()); @@ -792,7 +640,9 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo } mapbox::geometry::point<double> point = {x, y}; - return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) })); + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>( + env, + rendererFrontend->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) })); } jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesForBox(JNIEnv& env, jni::jfloat left, jni::jfloat top, @@ -810,11 +660,13 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo mapbox::geometry::point<double>{ right, bottom } }; - return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>( + env, + rendererFrontend->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); } jni::Object<Light> NativeMapView::getLight(JNIEnv& env) { - mbgl::style::Light* light = map->getLight(); + mbgl::style::Light* light = map->getStyle().getLight(); if (light) { return jni::Object<Light>(Light::createJavaLightPeer(env, *map, *light)); } else { @@ -825,7 +677,7 @@ jni::Object<Light> NativeMapView::getLight(JNIEnv& env) { jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { // Get the core layers - std::vector<style::Layer*> layers = map->getLayers(); + std::vector<style::Layer*> layers = map->getStyle().getLayers(); // Convert jni::Array<jni::Object<Layer>> jLayers = jni::Array<jni::Object<Layer>>::New(env, layers.size(), Layer::javaClass); @@ -843,7 +695,7 @@ jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { jni::Object<Layer> NativeMapView::getLayer(JNIEnv& env, jni::String layerId) { // Find the layer - mbgl::style::Layer* coreLayer = map->getLayer(jni::Make<std::string>(env, layerId)); + mbgl::style::Layer* coreLayer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId)); if (!coreLayer) { mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); return jni::Object<Layer>(); @@ -870,7 +722,7 @@ void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, jni::String Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); // Find the sibling - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); auto siblingId = jni::Make<std::string>(env, above); size_t index = 0; @@ -905,7 +757,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde assert(nativeLayerPtr != 0); Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); // Check index int numLayers = layers.size() - 1; @@ -928,7 +780,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde * Remove by layer id. */ jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) { - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(jni::Make<std::string>(env, id)); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(jni::Make<std::string>(env, id)); if (coreLayer) { return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); } else { @@ -940,7 +792,7 @@ jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) { * Remove layer at index. */ jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); // Check index int numLayers = layers.size() - 1; @@ -949,7 +801,7 @@ jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { return jni::Object<Layer>(); } - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layers.at(index)->getID()); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layers.at(index)->getID()); if (coreLayer) { return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); } else { @@ -964,7 +816,7 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { assert(layerPtr != 0); mbgl::android::Layer *layer = reinterpret_cast<mbgl::android::Layer *>(layerPtr); - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layer->get().getID()); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layer->get().getID()); if (coreLayer) { layer->setLayer(std::move(coreLayer)); } @@ -972,13 +824,13 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) { // Get the core sources - std::vector<style::Source*> sources = map->getSources(); + std::vector<style::Source*> sources = map->getStyle().getSources(); // Convert jni::Array<jni::Object<Source>> jSources = jni::Array<jni::Object<Source>>::New(env, sources.size(), Source::javaClass); int index = 0; for (auto source : sources) { - auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *map, *source)); + auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *source)); jSources.Set(env, index, jSource); jni::DeleteLocalRef(env, jSource); index++; @@ -989,14 +841,14 @@ jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) { jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId) { // Find the source - mbgl::style::Source* coreSource = map->getSource(jni::Make<std::string>(env, sourceId)); + mbgl::style::Source* coreSource = map->getStyle().getSource(jni::Make<std::string>(env, sourceId)); if (!coreSource) { mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); return jni::Object<Source>(); } // Create and return the source's native peer - return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); + return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource)); } void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) { @@ -1005,15 +857,16 @@ void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) { Source *source = reinterpret_cast<Source *>(sourcePtr); try { source->addToMap(*map); + source->setRendererFrontend(*rendererFrontend); } catch (const std::runtime_error& error) { jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); } } jni::Object<Source> NativeMapView::removeSourceById(JNIEnv& env, jni::String id) { - std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(jni::Make<std::string>(env, id)); + std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(jni::Make<std::string>(env, id)); if (coreSource) { - return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); + return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource)); } else { return jni::Object<Source>(); } @@ -1023,7 +876,7 @@ void NativeMapView::removeSource(JNIEnv&, jlong sourcePtr) { assert(sourcePtr != 0); mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr); - std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(source->get().getID()); + std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(source->get().getID()); if (coreSource) { source->setSource(std::move(coreSource)); } @@ -1040,415 +893,31 @@ void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::ji jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - map->addImage(jni::Make<std::string>(env, name), - std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale))); + map->getStyle().addImage(std::make_unique<mbgl::style::Image>( + jni::Make<std::string>(env, name), + std::move(premultipliedImage), + float(scale))); } void NativeMapView::removeImage(JNIEnv& env, jni::String name) { - map->removeImage(jni::Make<std::string>(env, name)); -} - -// Private methods // - -void NativeMapView::_initializeDisplay() { - assert(display == EGL_NO_DISPLAY); - assert(config == nullptr); - assert(format < 0); - - display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError()); - throw std::runtime_error("eglGetDisplay() failed"); - } - - EGLint major, minor; - if (!eglInitialize(display, &major, &minor)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError()); - throw std::runtime_error("eglInitialize() failed"); - } - if ((major <= 1) && (minor < 3)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major, - minor); - throw std::runtime_error("EGL version is too low"); - } - - // Detect if we are in emulator. - const bool inEmulator = []() { - char prop[PROP_VALUE_MAX]; - __system_property_get("ro.kernel.qemu", prop); - return strtol(prop, nullptr, 0) == 1; - }(); - - if (inEmulator) { - // XXX https://code.google.com/p/android/issues/detail?id=78977 - mbgl::Log::Warning(mbgl::Event::Android, "Running SDK in emulator!"); - } - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d", eglGetError()); - throw std::runtime_error("eglBindAPI() failed"); - } - - // Get all configs at least RGB 565 with 16 depth and 8 stencil - EGLint configAttribs[] = { - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BUFFER_SIZE, 16, - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, 8, - (inEmulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, - (inEmulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, - EGL_NONE - }; - - EGLint numConfigs; - if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d", - eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - if (numConfigs < 1) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs."); - throw std::runtime_error("eglChooseConfig() failed"); - } - - const auto configs = std::make_unique<EGLConfig[]>(numConfigs); - if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - - config = chooseConfig(configs.get(), numConfigs); - if (config == nullptr) { - mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen"); - throw std::runtime_error("No config chosen"); - } - - if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } -} - -// Quality -typedef enum { - Format16Bit = 3, - Format32BitNoAlpha = 1, - Format32BitAlpha = 2, - Format24Bit = 0, - Unknown = 4 -} BufferFormat; - -typedef enum { - Format16Depth8Stencil = 1, - Format24Depth8Stencil = 0, -} DepthStencilFormat; - -// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num, -// config_id> -typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties; - -EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) { - // Create a list of configs that pass our filters - std::list<ConfigProperties> configList; - for (int i = 0; i < numConfigs; i++) { - EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil, - sampleBuffers, samples; - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - bool configOk = true; - configOk &= (depth == 24) || (depth == 16); - configOk &= stencil == 8; - configOk &= sampleBuffers == 0; - configOk &= samples == 0; - - // Filter our configs first for depth, stencil and anti-aliasing - if (configOk) { - // Work out the config's buffer format - BufferFormat bufferFormat; - if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) { - bufferFormat = Format16Bit; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format32BitNoAlpha; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) { - bufferFormat = Format32BitAlpha; - } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format24Bit; - } else { - bufferFormat = Unknown; - } - - // Work out the config's depth stencil format - DepthStencilFormat depthStencilFormat; - if ((depth == 16) && (stencil == 8)) { - depthStencilFormat = Format16Depth8Stencil; - } else { - depthStencilFormat = Format24Depth8Stencil; - } - - bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT; - bool isCaveat = caveat != EGL_NONE; - EGLConfig configId = configs[i]; - - // Ignore formats we don't recognise - if (bufferFormat != Unknown) { - configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat, - isNotConformant, isCaveat, i, configId)); - } - } - } - - if (configList.empty()) { - mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty."); - } - - // Sort the configs to find the best one - configList.sort(); - bool isConformant = !std::get<2>(configList.front()); - bool isCaveat = std::get<3>(configList.front()); - EGLConfig configId = std::get<5>(configList.front()); - - if (isCaveat) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat."); - } - if (!isConformant) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant."); - } - - return configId; + map->getStyle().removeImage(jni::Make<std::string>(env, name)); } -void NativeMapView::_terminateDisplay() { - if (display != EGL_NO_DISPLAY) { - // Destroy the surface first, if it still exists. This call needs a valid surface. - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - surface = EGL_NO_SURFACE; - } - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglTerminate(display)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", - eglGetError()); - throw std::runtime_error("eglTerminate() failed"); - } - } - - display = EGL_NO_DISPLAY; - config = nullptr; - format = -1; -} - -void NativeMapView::_initializeContext() { - assert(display != EGL_NO_DISPLAY); - assert(context == EGL_NO_CONTEXT); - assert(config != nullptr); - - const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); - if (context == EGL_NO_CONTEXT) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateContext() failed"); - } -} - -void NativeMapView::_terminateContext() { - if (display != EGL_NO_DISPLAY) { - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (context != EGL_NO_CONTEXT) { - if (!eglDestroyContext(display, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroyContext() failed"); - } - } - } - - context = EGL_NO_CONTEXT; -} - -void NativeMapView::_createSurface(ANativeWindow *window_) { - assert(window == nullptr); - assert(window_ != nullptr); - window = window_; - - assert(display != EGL_NO_DISPLAY); - assert(surface == EGL_NO_SURFACE); - assert(config != nullptr); - assert(format >= 0); - - ANativeWindow_setBuffersGeometry(window, 0, 0, format); - - const EGLint surfaceAttribs[] = {EGL_NONE}; - surface = eglCreateWindowSurface(display, config, window, surfaceAttribs); - if (surface == EGL_NO_SURFACE) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateWindowSurface() failed"); - } - - if (firstRender) { - firstRender = false; - - BackendScope guard(*this); - - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } -} - -void NativeMapView::_destroySurface() { - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - } - - surface = EGL_NO_SURFACE; - firstRender = true; - - if (window != nullptr) { - ANativeWindow_release(window); - window = nullptr; +jni::Object<Bitmap> NativeMapView::getImage(JNIEnv& env, jni::String name) { + const mbgl::style::Image *image = map->getStyle().getImage(jni::Make<std::string>(env, name)); + if (image) { + return Bitmap::CreateBitmap(env, image->getImage()); + } else { + return jni::Object<Bitmap>(); } } -mbgl::Size NativeMapView::getFramebufferSize() const { - return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; +void NativeMapView::setPrefetchesTiles(JNIEnv&, jni::jboolean enable) { + map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0)); } -void NativeMapView::updateAssumedState() { - assumeFramebufferBinding(0); - assumeViewportSize(getFramebufferSize()); -} - -void NativeMapView::updateFps() { - if (!fpsEnabled) { - return; - } - - static int frames = 0; - static int64_t timeElapsed = 0LL; - - frames++; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec; - - if (currentTime - timeElapsed >= 1) { - fps = frames / ((currentTime - timeElapsed) / 1E9); - mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps); - timeElapsed = currentTime; - frames = 0; - } - - assert(vm != nullptr); - - android::UniqueEnv _env = android::AttachEnv(); - static auto onFpsChanged = javaClass.GetMethod<void (double)>(*_env, "onFpsChanged"); - javaPeer->Call(*_env, onFpsChanged, fps); +jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) { + return jni::jboolean(map->getPrefetchZoomDelta() > 0); } // Static methods // @@ -1463,19 +932,10 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { // Register the peer jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr", - std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::String, jni::jint, jni::jlong>, + std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::Object<MapRenderer>, jni::jfloat>, "nativeInitialize", "nativeDestroy", - METHOD(&NativeMapView::render, "nativeRender"), - METHOD(&NativeMapView::update, "nativeUpdate"), METHOD(&NativeMapView::resizeView, "nativeResizeView"), - METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"), - METHOD(&NativeMapView::initializeDisplay, "nativeInitializeDisplay"), - METHOD(&NativeMapView::terminateDisplay, "nativeTerminateDisplay"), - METHOD(&NativeMapView::initializeContext, "nativeInitializeContext"), - METHOD(&NativeMapView::terminateContext, "nativeTerminateContext"), - METHOD(&NativeMapView::createSurface, "nativeCreateSurface"), - METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"), METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"), METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"), METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"), @@ -1508,7 +968,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"), METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"), METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"), - METHOD(&NativeMapView::enableFps, "nativeSetEnableFps"), METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"), METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"), METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"), @@ -1528,6 +987,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"), METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"), METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"), + METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"), METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"), METHOD(&NativeMapView::getTransitionDuration, "nativeGetTransitionDuration"), METHOD(&NativeMapView::setTransitionDuration, "nativeSetTransitionDuration"), @@ -1552,9 +1012,12 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::removeSource, "nativeRemoveSource"), METHOD(&NativeMapView::addImage, "nativeAddImage"), METHOD(&NativeMapView::removeImage, "nativeRemoveImage"), - METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds") + METHOD(&NativeMapView::getImage, "nativeGetImage"), + METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"), + METHOD(&NativeMapView::setPrefetchesTiles, "nativeSetPrefetchesTiles"), + METHOD(&NativeMapView::getPrefetchesTiles, "nativeGetPrefetchesTiles") ); } -} -} +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index c638de44fb..72c7b1a9eb 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -1,17 +1,14 @@ #pragma once -#include <mbgl/map/backend.hpp> #include <mbgl/map/change.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/map.hpp> -#include <mbgl/map/view.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> -#include "file_source.hpp" #include "annotation/marker.hpp" #include "annotation/polygon.hpp" #include "annotation/polyline.hpp" @@ -25,6 +22,7 @@ #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" #include "style/light.hpp" +#include "bitmap.hpp" #include <exception> #include <string> @@ -36,7 +34,11 @@ namespace mbgl { namespace android { -class NativeMapView : public View, public Backend { +class AndroidRendererFrontend; +class FileSource; +class MapRenderer; + +class NativeMapView : public MapObserver { public: static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; }; @@ -48,26 +50,15 @@ public: NativeMapView(jni::JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, - jni::jfloat pixelRatio, - jni::String programCacheDir, - jni::jint availableProcessors, - jni::jlong totalMemory); + jni::Object<MapRenderer>, + jni::jfloat pixelRatio); virtual ~NativeMapView(); - // mbgl::View // - - void bind() override; - - // mbgl::Backend // - - void updateAssumedState() override; - void invalidate() override; - // Deprecated // void notifyMapChange(mbgl::MapChange); - // mbgl::Backend (mbgl::MapObserver) // + // mbgl::RendererBackend (mbgl::MapObserver) // void onCameraWillChange(MapObserver::CameraChangeMode) override; void onCameraIsChanging() override; void onCameraDidChange(MapObserver::CameraChangeMode) override; @@ -83,28 +74,8 @@ public: // JNI // - void destroy(jni::JNIEnv&); - - void render(jni::JNIEnv&); - - void update(jni::JNIEnv&); - void resizeView(jni::JNIEnv&, int, int); - void resizeFramebuffer(jni::JNIEnv&, int, int); - - void initializeDisplay(jni::JNIEnv&); - - void terminateDisplay(jni::JNIEnv&); - - void initializeContext(jni::JNIEnv&); - - void terminateContext(jni::JNIEnv&); - - void createSurface(jni::JNIEnv&, jni::Object<>); - - void destroySurface(jni::JNIEnv&); - jni::String getStyleUrl(jni::JNIEnv&); void setStyleUrl(jni::JNIEnv&, jni::String); @@ -171,8 +142,6 @@ public: void scheduleSnapshot(jni::JNIEnv&); - void enableFps(jni::JNIEnv&, jni::jboolean enable); - jni::Object<CameraPosition> getCameraPosition(jni::JNIEnv&); void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String); @@ -211,6 +180,8 @@ public: void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>); + void removeAnnotationIcon(JNIEnv&, jni::String); + jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String); jni::jlong getTransitionDuration(JNIEnv&); @@ -263,72 +234,28 @@ public: void removeImage(JNIEnv&, jni::String); -protected: - // mbgl::Backend // + jni::Object<Bitmap> getImage(JNIEnv&, jni::String); - gl::ProcAddress initializeExtension(const char*) override; - void activate() override; - void deactivate() override; + void setPrefetchesTiles(JNIEnv&, jni::jboolean); -private: - void _initializeDisplay(); - - void _terminateDisplay(); - - void _initializeContext(); - - void _terminateContext(); - - void _createSurface(ANativeWindow*); - - void _destroySurface(); - - EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); - - mbgl::Size getFramebufferSize() const; - - void updateFps(); + jni::jboolean getPrefetchesTiles(JNIEnv&); private: - void recalculateSourceTileCacheSize(); + std::unique_ptr<AndroidRendererFrontend> rendererFrontend; JavaVM *vm = nullptr; jni::UniqueWeakObject<NativeMapView> javaPeer; + MapRenderer& mapRenderer; + std::string styleUrl; std::string apiKey; - ANativeWindow *window = nullptr; - - EGLConfig config = nullptr; - EGLint format = -1; - - EGLDisplay oldDisplay = EGL_NO_DISPLAY; - EGLSurface oldReadSurface = EGL_NO_SURFACE; - EGLSurface oldDrawSurface = EGL_NO_SURFACE; - EGLContext oldContext = EGL_NO_CONTEXT; - - EGLDisplay display = EGL_NO_DISPLAY; - EGLSurface surface = EGL_NO_SURFACE; - EGLContext context = EGL_NO_CONTEXT; - - float pixelRatio; - bool fpsEnabled = false; - bool snapshot = false; - bool firstRender = true; - double fps = 0.0; // Minimum texture size according to OpenGL ES 2.0 specification. int width = 64; int height = 64; - int fbWidth = 64; - int fbHeight = 64; - - bool framebufferSizeChanged = true; - - int availableProcessors = 0; - size_t totalMemory = 0; // Ensure these are initialised last std::shared_ptr<mbgl::ThreadPool> threadPool; diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 49d28f2ebb..dff7d1d984 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -1,10 +1,10 @@ #include "run_loop_impl.hpp" #include <mbgl/util/platform.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/thread_context.hpp> #include <mbgl/util/thread_local.hpp> +#include <mbgl/util/thread.hpp> #include <mbgl/util/timer.hpp> +#include <mbgl/actor/scheduler.hpp> #include <android/looper.h> @@ -24,7 +24,6 @@ namespace { using namespace mbgl::util; -static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; int looperCallbackNew(int fd, int, void* data) { int buffer[1]; @@ -62,9 +61,13 @@ namespace util { // timeout, but on the main thread `ALooper_pollAll` is called by the activity // automatically, thus we cannot set the timeout. Instead we wake the loop // with an external file descriptor event coming from this thread. +// +// Usually an actor should not carry pointers to other threads, but in +// this case the RunLoop itself owns the Alarm and calling wake() is the most +// efficient way of waking up the RunLoop and it is also thread-safe. class Alarm { public: - Alarm(RunLoop::Impl* loop_) : loop(loop_) {} + Alarm(ActorRef<Alarm>, RunLoop::Impl* loop_) : loop(loop_) {} void set(const Milliseconds& timeout) { alarm.start(timeout, mbgl::Duration::zero(), [this]() { loop->wake(); }); @@ -102,7 +105,7 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) { case Type::Default: ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, looperCallbackDefault, this); - alarm = std::make_unique<Thread<Alarm>>(ThreadContext{"Alarm"}, this); + alarm = std::make_unique<Thread<Alarm>>("Alarm", this); running = true; break; } @@ -190,26 +193,27 @@ Milliseconds RunLoop::Impl::processRunnables() { auto timeout = std::chrono::duration_cast<Milliseconds>(nextDue - now); if (alarm) { - alarm->invoke(&Alarm::set, timeout); + alarm->actor().invoke(&Alarm::set, timeout); } return timeout; } RunLoop* RunLoop::Get() { - return current.get(); + assert(static_cast<RunLoop*>(Scheduler::GetCurrent())); + return static_cast<RunLoop*>(Scheduler::GetCurrent()); } RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) { - current.set(this); + Scheduler::SetCurrent(this); } RunLoop::~RunLoop() { - current.set(nullptr); + Scheduler::SetCurrent(nullptr); } LOOP_HANDLE RunLoop::getLoopHandle() { - return current.get()->impl.get(); + return Get()->impl.get(); } void RunLoop::push(std::shared_ptr<WorkTask> task) { diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp new file mode 100644 index 0000000000..d64218d11a --- /dev/null +++ b/platform/android/src/snapshotter/map_snapshotter.cpp @@ -0,0 +1,110 @@ +#include "map_snapshotter.hpp" + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include "../attach_env.hpp" +#include "../bitmap.hpp" + +namespace mbgl { +namespace android { + +MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env, + jni::Object<MapSnapshotter> _obj, + jni::Object<FileSource> jFileSource, + jni::jfloat _pixelRatio, + jni::jint width, + jni::jint height, + jni::String styleURL, + jni::Object<LatLngBounds> region, + jni::Object<CameraPosition> position, + jni::String _programCacheDir) + : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapSnapshotter>(jni::NewWeakGlobalRef(_env, _obj.Get()).release()))) + , pixelRatio(_pixelRatio) + , threadPool(sharedThreadPool()) { + + // Get a reference to the JavaVM for callbacks + if (_env.GetJavaVM(&vm) < 0) { + _env.ExceptionDescribe(); + return; + } + + auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource); + auto size = mbgl::Size { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }; + auto cameraOptions = position ? CameraPosition::getCameraOptions(_env, position) : CameraOptions(); + optional<mbgl::LatLngBounds> bounds; + if (region) { + bounds = LatLngBounds::getLatLngBounds(_env, region); + } + + // Create the core snapshotter + snapshotter = std::make_unique<mbgl::MapSnapshotter>(fileSource, + *threadPool, + jni::Make<std::string>(_env, styleURL), + size, + pixelRatio, + cameraOptions, + bounds, + jni::Make<std::string>(_env, _programCacheDir)); + +} + +MapSnapshotter::~MapSnapshotter() = default; + +void MapSnapshotter::start(JNIEnv&) { + MBGL_VERIFY_THREAD(tid); + + snapshotCallback = std::make_unique<Actor<mbgl::MapSnapshotter::Callback>>(*Scheduler::GetCurrent(), [this](std::exception_ptr err, PremultipliedImage image) { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + + if (err) { + // error handler callback + static auto onSnapshotFailed = javaClass.GetMethod<void (jni::String)>(*_env, "onSnapshotFailed"); + javaPeer->Call(*_env, onSnapshotFailed, jni::Make<jni::String>(*_env, util::toString(err))); + } else { + // Create the bitmap + auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image)); + + // invoke callback + static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); + javaPeer->Call(*_env, onSnapshotReady, bitmap); + } + }); + + snapshotter->snapshot(snapshotCallback->self()); +} + +void MapSnapshotter::cancel(JNIEnv&) { + MBGL_VERIFY_THREAD(tid); + + snapshotCallback.reset(); + snapshotter.reset(); +} + +// Static methods // + +jni::Class<MapSnapshotter> MapSnapshotter::javaClass; + +void MapSnapshotter::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapSnapshotter::javaClass = *jni::Class<MapSnapshotter>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<MapSnapshotter>(env, MapSnapshotter::javaClass, "nativePtr", + std::make_unique<MapSnapshotter, JNIEnv&, jni::Object<MapSnapshotter>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jint, jni::String, jni::Object<LatLngBounds>, jni::Object<CameraPosition>, jni::String>, + "nativeInitialize", + "finalize", + METHOD(&MapSnapshotter::start, "nativeStart"), + METHOD(&MapSnapshotter::cancel, "nativeCancel") + ); +} + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/snapshotter/map_snapshotter.hpp b/platform/android/src/snapshotter/map_snapshotter.hpp new file mode 100644 index 0000000000..093f589c05 --- /dev/null +++ b/platform/android/src/snapshotter/map_snapshotter.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <mbgl/map/map_snapshotter.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/util.hpp> + +#include "../file_source.hpp" +#include "../geometry/lat_lng_bounds.hpp" +#include "../map/camera_position.hpp" + +#include <jni/jni.hpp> +#include "../jni/generic_global_ref_deleter.hpp" + +#include <memory> + +namespace mbgl { +namespace android { + +class SnapshotterRendererFrontend; + +class MapSnapshotter { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshotter"; }; + + static jni::Class<MapSnapshotter> javaClass; + + static void registerNative(jni::JNIEnv&); + + MapSnapshotter(jni::JNIEnv&, + jni::Object<MapSnapshotter>, + jni::Object<FileSource>, + jni::jfloat pixelRatio, + jni::jint width, + jni::jint height, + jni::String styleURL, + jni::Object<LatLngBounds> region, + jni::Object<CameraPosition> position, + jni::String programCacheDir); + + ~MapSnapshotter(); + + void start(JNIEnv&); + + void cancel(JNIEnv&); + +private: + MBGL_STORE_THREAD(tid); + + JavaVM *vm = nullptr; + GenericUniqueWeakObject<MapSnapshotter> javaPeer; + + float pixelRatio; + + std::shared_ptr<mbgl::ThreadPool> threadPool; + std::unique_ptr<Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback; + std::unique_ptr<mbgl::MapSnapshotter> snapshotter; +}; + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp index e2b2685928..082fe411e2 100644 --- a/platform/android/src/style/android_conversion.hpp +++ b/platform/android/src/style/android_conversion.hpp @@ -2,6 +2,7 @@ #include "value.hpp" +#include <mbgl/util/feature.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/util/optional.hpp> @@ -66,6 +67,14 @@ inline optional<float> toNumber(const mbgl::android::Value& value) { } } +inline optional<double> toDouble(const mbgl::android::Value& value) { + if (value.isNumber()) { + return value.toDouble(); + } else { + return {}; + } +} + inline optional<std::string> toString(const mbgl::android::Value& value) { if (value.isString()) { return value.toString(); diff --git a/platform/android/src/style/conversion/latlngquad.hpp b/platform/android/src/style/conversion/latlngquad.hpp new file mode 100644 index 0000000000..9d1a83e164 --- /dev/null +++ b/platform/android/src/style/conversion/latlngquad.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <mapbox/geojson.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +optional<std::array<LatLng, 4>> Converter<std::array<LatLng, 4>>::operator()(const mbgl::android::Value& value, Error& error) const { + if (value.isNull() || !value.isArray()) { + error = { "value cannot be converted to LatLng array" }; + return {}; + } + + return convert<GeoJSON>(value.toString(), error); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/platform/android/src/style/conversion/transition_options.hpp b/platform/android/src/style/conversion/transition_options.hpp index 3614878f43..ae65a32194 100644 --- a/platform/android/src/style/conversion/transition_options.hpp +++ b/platform/android/src/style/conversion/transition_options.hpp @@ -3,6 +3,7 @@ #include "../../conversion/conversion.hpp" #include <jni/jni.hpp> +#include <mbgl/style/transition_options.hpp> #include "../../jni/local_object.hpp" #include "../transition_options.hpp" diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp index a00f668c24..375d1a33aa 100644 --- a/platform/android/src/style/conversion/types.hpp +++ b/platform/android/src/style/conversion/types.hpp @@ -58,15 +58,15 @@ struct Converter<jni::jobject*, mbgl::style::IconTextFitType> { }; template <> -struct Converter<jni::jobject*, mbgl::style::TextJustifyType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const { +struct Converter<jni::jobject*, mbgl::style::SymbolAnchorType> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolAnchorType& value) const { return convert<jni::jobject*, std::string>(env, toString(value)); } }; template <> -struct Converter<jni::jobject*, mbgl::style::TextAnchorType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextAnchorType& value) const { +struct Converter<jni::jobject*, mbgl::style::TextJustifyType> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const { return convert<jni::jobject*, std::string>(env, toString(value)); } }; diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp index e96de3b01e..a19ca33a2f 100644 --- a/platform/android/src/style/conversion/types_string_values.hpp +++ b/platform/android/src/style/conversion/types_string_values.hpp @@ -109,51 +109,34 @@ namespace conversion { } } - // text-justify - inline std::string toString(mbgl::style::TextJustifyType value) { - switch (value) { - case mbgl::style::TextJustifyType::Left: - return "left"; - break; - case mbgl::style::TextJustifyType::Center: - return "center"; - break; - case mbgl::style::TextJustifyType::Right: - return "right"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - // text-anchor - inline std::string toString(mbgl::style::TextAnchorType value) { + // icon-anchor + inline std::string toString(mbgl::style::SymbolAnchorType value) { switch (value) { - case mbgl::style::TextAnchorType::Center: + case mbgl::style::SymbolAnchorType::Center: return "center"; break; - case mbgl::style::TextAnchorType::Left: + case mbgl::style::SymbolAnchorType::Left: return "left"; break; - case mbgl::style::TextAnchorType::Right: + case mbgl::style::SymbolAnchorType::Right: return "right"; break; - case mbgl::style::TextAnchorType::Top: + case mbgl::style::SymbolAnchorType::Top: return "top"; break; - case mbgl::style::TextAnchorType::Bottom: + case mbgl::style::SymbolAnchorType::Bottom: return "bottom"; break; - case mbgl::style::TextAnchorType::TopLeft: + case mbgl::style::SymbolAnchorType::TopLeft: return "top-left"; break; - case mbgl::style::TextAnchorType::TopRight: + case mbgl::style::SymbolAnchorType::TopRight: return "top-right"; break; - case mbgl::style::TextAnchorType::BottomLeft: + case mbgl::style::SymbolAnchorType::BottomLeft: return "bottom-left"; break; - case mbgl::style::TextAnchorType::BottomRight: + case mbgl::style::SymbolAnchorType::BottomRight: return "bottom-right"; break; default: @@ -161,6 +144,23 @@ namespace conversion { } } + // text-justify + inline std::string toString(mbgl::style::TextJustifyType value) { + switch (value) { + case mbgl::style::TextJustifyType::Left: + return "left"; + break; + case mbgl::style::TextJustifyType::Center: + return "center"; + break; + case mbgl::style::TextJustifyType::Right: + return "right"; + break; + default: + throw std::runtime_error("Not implemented"); + } + } + // text-transform inline std::string toString(mbgl::style::TextTransformType value) { switch (value) { diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp index 96a9356679..4c7f69e956 100644 --- a/platform/android/src/style/layers/circle_layer.cpp +++ b/platform/android/src/style/layers/circle_layer.cpp @@ -142,6 +142,12 @@ namespace android { return jni::Object<jni::ObjectTag>(*converted); } + jni::Object<jni::ObjectTag> CircleLayer::getCirclePitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCirclePitchAlignment()); + return jni::Object<jni::ObjectTag>(*converted); + } + jni::Object<jni::ObjectTag> CircleLayer::getCircleStrokeWidth(jni::JNIEnv& env) { using namespace mbgl::android::conversion; Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleStrokeWidth()); @@ -236,6 +242,7 @@ namespace android { METHOD(&CircleLayer::getCircleTranslate, "nativeGetCircleTranslate"), METHOD(&CircleLayer::getCircleTranslateAnchor, "nativeGetCircleTranslateAnchor"), METHOD(&CircleLayer::getCirclePitchScale, "nativeGetCirclePitchScale"), + METHOD(&CircleLayer::getCirclePitchAlignment, "nativeGetCirclePitchAlignment"), METHOD(&CircleLayer::getCircleStrokeWidthTransition, "nativeGetCircleStrokeWidthTransition"), METHOD(&CircleLayer::setCircleStrokeWidthTransition, "nativeSetCircleStrokeWidthTransition"), METHOD(&CircleLayer::getCircleStrokeWidth, "nativeGetCircleStrokeWidth"), diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp index 81737e8996..9d323e92bb 100644 --- a/platform/android/src/style/layers/circle_layer.hpp +++ b/platform/android/src/style/layers/circle_layer.hpp @@ -53,6 +53,8 @@ public: jni::Object<jni::ObjectTag> getCirclePitchScale(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getCirclePitchAlignment(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getCircleStrokeWidth(jni::JNIEnv&); void setCircleStrokeWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); jni::Object<TransitionOptions> getCircleStrokeWidthTransition(jni::JNIEnv&); diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp index 9bdc308d85..51a48520bf 100644 --- a/platform/android/src/style/layers/custom_layer.cpp +++ b/platform/android/src/style/layers/custom_layer.cpp @@ -7,11 +7,12 @@ namespace mbgl { namespace android { - CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong deinitializeFunction, jni::jlong context) + CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context) : Layer(env, std::make_unique<mbgl::style::CustomLayer>( jni::Make<std::string>(env, layerId), reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initializeFunction), reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(renderFunction), + reinterpret_cast<mbgl::style::CustomLayerContextLostFunction>(contextLostFunction), reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitializeFunction), reinterpret_cast<void*>(context)) ) { @@ -21,6 +22,10 @@ namespace android { : Layer(map, coreLayer) { } + CustomLayer::CustomLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::CustomLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + CustomLayer::~CustomLayer() = default; void CustomLayer::update(jni::JNIEnv&) { @@ -48,7 +53,7 @@ namespace android { // Register the peer jni::RegisterNativePeer<CustomLayer>( env, CustomLayer::javaClass, "nativePtr", - std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong>, + std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong>, "initialize", "finalize", METHOD(&CustomLayer::update, "nativeUpdate")); diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp index 1173d21bfd..9e079c1288 100644 --- a/platform/android/src/style/layers/custom_layer.hpp +++ b/platform/android/src/style/layers/custom_layer.hpp @@ -16,10 +16,12 @@ public: static void registerNative(jni::JNIEnv&); - CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong); + CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong); CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&); + CustomLayer(mbgl::Map&, std::unique_ptr<mbgl::style::CustomLayer>); + ~CustomLayer(); void update(jni::JNIEnv&); diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index d571c3fd2e..02a1f0be82 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -3,6 +3,7 @@ #include <jni/jni.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/util/logging.hpp> @@ -53,7 +54,7 @@ namespace android { } // Add layer to map - _map.addLayer(releaseCoreLayer(), before); + _map.getStyle().addLayer(releaseCoreLayer(), before); // Save pointer to the map this->map = &_map; @@ -91,19 +92,31 @@ namespace android { Value value(env, jvalue); // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value, mbgl::optional<std::string>()); + optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; } } + struct SetFilterEvaluator { + style::Filter filter; + + void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support filters"); } + void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support filters"); } + void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support filters"); } + + template <class LayerType> + void operator()(LayerType& layer) { + layer.setFilter(filter); + } + }; + void Layer::setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) { using namespace mbgl::style; using namespace mbgl::style::conversion; Value wrapped(env, jfilter); - Filter filter; Error error; optional<Filter> converted = convert<Filter>(wrapped, error); @@ -111,62 +124,45 @@ namespace android { mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message); return; } - filter = std::move(*converted); - - if (layer.is<FillLayer>()) { - layer.as<FillLayer>()->setFilter(filter); - } else if (layer.is<LineLayer>()) { - layer.as<LineLayer>()->setFilter(filter); - } else if (layer.is<SymbolLayer>()) { - layer.as<SymbolLayer>()->setFilter(filter); - } else if (layer.is<CircleLayer>()) { - layer.as<CircleLayer>()->setFilter(filter); - } else if (layer.is<FillExtrusionLayer>()){ - layer.as<FillExtrusionLayer>()->setFilter(filter); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support filters"); - } + + layer.accept(SetFilterEvaluator {std::move(*converted)}); } - void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) { - using namespace mbgl::style; + struct SetSourceLayerEvaluator { + std::string sourceLayer; - std::string layerId = jni::Make<std::string>(env, sourceLayer); - - if (layer.is<FillLayer>()) { - layer.as<FillLayer>()->setSourceLayer(layerId); - } else if (layer.is<LineLayer>()) { - layer.as<LineLayer>()->setSourceLayer(layerId); - } else if (layer.is<SymbolLayer>()) { - layer.as<SymbolLayer>()->setSourceLayer(layerId); - } else if (layer.is<CircleLayer>()) { - layer.as<CircleLayer>()->setSourceLayer(layerId); - } else if(layer.is<FillExtrusionLayer>()) { - layer.as<FillExtrusionLayer>()->setSourceLayer(layerId); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); + void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support source layer"); } + void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support source layer"); } + void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support source layer"); } + + template <class LayerType> + void operator()(LayerType& layer) { + layer.setSourceLayer(sourceLayer); } + }; + + void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) { + layer.accept(SetSourceLayerEvaluator {jni::Make<std::string>(env, sourceLayer)}); } - jni::String Layer::getSourceLayer(jni::JNIEnv& env) { - using namespace mbgl::style; + struct GetSourceLayerEvaluator { + std::string noop(std::string layerType) { + Log::Warning(mbgl::Event::JNI, "%s doesn't support source layer", layerType.c_str()); + return {}; + } - std::string sourceLayerId; - if (layer.is<FillLayer>()) { - sourceLayerId = layer.as<FillLayer>()->getSourceLayer(); - } else if (layer.is<LineLayer>()) { - sourceLayerId = layer.as<LineLayer>()->getSourceLayer(); - } else if (layer.is<SymbolLayer>()) { - sourceLayerId = layer.as<SymbolLayer>()->getSourceLayer(); - } else if (layer.is<CircleLayer>()) { - sourceLayerId = layer.as<CircleLayer>()->getSourceLayer(); - } else if (layer.is<FillExtrusionLayer>()) { - sourceLayerId = layer.as<FillExtrusionLayer>()->getSourceLayer(); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); + std::string operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); } + std::string operator()(style::CustomLayer&) { return noop("CustomLayer"); } + std::string operator()(style::RasterLayer&) { return noop("RasterLayer"); } + + template <class LayerType> + std::string operator()(LayerType& layer) { + return layer.getSourceLayer(); } + }; - return jni::Make<jni::String>(env, sourceLayerId); + jni::String Layer::getSourceLayer(jni::JNIEnv& env) { + return jni::Make<jni::String>(env, layer.accept(GetSourceLayerEvaluator())); } jni::jfloat Layer::getMinZoom(jni::JNIEnv&){ diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp index 5c49f875ee..9803b6f841 100644 --- a/platform/android/src/style/layers/layers.cpp +++ b/platform/android/src/style/layers/layers.cpp @@ -24,53 +24,51 @@ namespace mbgl { namespace android { -static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { - if (coreLayer.is<mbgl::style::BackgroundLayer>()) { - return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>()); - } else if (coreLayer.is<mbgl::style::CircleLayer>()) { - return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>()); - } else if (coreLayer.is<mbgl::style::FillExtrusionLayer>()) { - return new FillExtrusionLayer(map, *coreLayer.as<mbgl::style::FillExtrusionLayer>()); - } else if (coreLayer.is<mbgl::style::FillLayer>()) { - return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>()); - } else if (coreLayer.is<mbgl::style::LineLayer>()) { - return new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>()); - } else if (coreLayer.is<mbgl::style::RasterLayer>()) { - return new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>()); - } else if (coreLayer.is<mbgl::style::SymbolLayer>()) { - return new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>()); - } else if (coreLayer.is<mbgl::style::CustomLayer>()) { - return new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>()); - } else { - return new UnknownLayer(map, coreLayer); +// Mapping from style layers to peer classes +template <class> struct PeerType {}; +template <> struct PeerType<style::BackgroundLayer> { using Type = android::BackgroundLayer; }; +template <> struct PeerType<style::CircleLayer> { using Type = android::CircleLayer; }; +template <> struct PeerType<style::FillExtrusionLayer> { using Type = android::FillExtrusionLayer; }; +template <> struct PeerType<style::FillLayer> { using Type = android::FillLayer; }; +template <> struct PeerType<style::LineLayer> { using Type = android::LineLayer; }; +template <> struct PeerType<style::RasterLayer> { using Type = android::RasterLayer; }; +template <> struct PeerType<style::SymbolLayer> { using Type = android::SymbolLayer; }; +template <> struct PeerType<style::CustomLayer> { using Type = android::CustomLayer; }; + +// Inititalizes a non-owning peer +struct LayerPeerIntitializer { + mbgl::Map& map; + + template <class LayerType> + Layer* operator()(LayerType& layer) { + return new typename PeerType<LayerType>::Type(map, layer); } -} +}; -template <class LayerType, class PeerType> -static PeerType* createPeer(Map& map, std::unique_ptr<mbgl::style::Layer> layer) { - return new PeerType(map, std::move(std::unique_ptr<LayerType>(layer.release()->as<LayerType>()))); +static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { + Layer* layer = coreLayer.accept(LayerPeerIntitializer {map}); + return layer ? layer : new UnknownLayer(map, coreLayer); } -static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { - if (coreLayer->is<style::BackgroundLayer>()) { - return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::CircleLayer>()) { - return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::FillExtrusionLayer>()) { - return createPeer<style::FillExtrusionLayer, FillExtrusionLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::FillLayer>()) { - return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::LineLayer>()) { - return createPeer<style::LineLayer, LineLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::RasterLayer>()) { - return createPeer<style::RasterLayer, RasterLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::SymbolLayer>()) { - return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<mbgl::style::CustomLayer>()) { - return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); - } else { - return new UnknownLayer(map, std::move(coreLayer)); +// Initializes an owning peer +// Only usable once since it needs to pass on ownership +// of the given layer and thus enforced to be an rvalue +struct UniqueLayerPeerIntitializer { + mbgl::Map& map; + std::unique_ptr<style::Layer> layer; + + template <class LayerType> + Layer* operator()(LayerType&) && { + return new typename PeerType<LayerType>::Type( + map, + std::unique_ptr<LayerType>(layer.release()->as<LayerType>()) + ); } +}; + +static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { + Layer* layer = coreLayer->accept(UniqueLayerPeerIntitializer {map, std::move(coreLayer)}); + return layer ? layer : new UnknownLayer(map, std::move(coreLayer)); } jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, Map& map, style::Layer& coreLayer) { diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp index 3a560a5deb..d44744a6cf 100644 --- a/platform/android/src/style/layers/symbol_layer.cpp +++ b/platform/android/src/style/layers/symbol_layer.cpp @@ -125,6 +125,18 @@ namespace android { return jni::Object<jni::ObjectTag>(*converted); } + jni::Object<jni::ObjectTag> SymbolLayer::getIconAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconAnchor()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<jni::ObjectTag> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPitchAlignment()); + return jni::Object<jni::ObjectTag>(*converted); + } + jni::Object<jni::ObjectTag> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) { using namespace mbgl::android::conversion; Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextPitchAlignment()); @@ -514,6 +526,8 @@ namespace android { METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"), METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"), METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"), + METHOD(&SymbolLayer::getIconAnchor, "nativeGetIconAnchor"), + METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"), METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"), METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"), METHOD(&SymbolLayer::getTextField, "nativeGetTextField"), diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp index 8366051c6e..417e5e143f 100644 --- a/platform/android/src/style/layers/symbol_layer.hpp +++ b/platform/android/src/style/layers/symbol_layer.hpp @@ -59,6 +59,10 @@ public: jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getIconAnchor(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getIconPitchAlignment(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&); jni::Object<jni::ObjectTag> getTextRotationAlignment(jni::JNIEnv&); diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 780cc4b6f6..90ef851eba 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -1,5 +1,7 @@ #include "geojson_source.hpp" +#include <mbgl/renderer/query.hpp> + // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" @@ -41,8 +43,8 @@ namespace android { ) { } - GeoJSONSource::GeoJSONSource(mbgl::Map& map, mbgl::style::GeoJSONSource& coreSource) - : Source(map, coreSource) { + GeoJSONSource::GeoJSONSource(mbgl::style::GeoJSONSource& coreSource) + : Source(coreSource) { } GeoJSONSource::~GeoJSONSource() = default; @@ -108,8 +110,8 @@ namespace android { using namespace mbgl::android::geojson; std::vector<mbgl::Feature> features; - if (map) { - features = map->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) }); + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) }); } return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features); } diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp index 938a20612c..52dd632bfa 100644 --- a/platform/android/src/style/sources/geojson_source.hpp +++ b/platform/android/src/style/sources/geojson_source.hpp @@ -21,7 +21,7 @@ public: GeoJSONSource(jni::JNIEnv&, jni::String, jni::Object<>); - GeoJSONSource(mbgl::Map&, mbgl::style::GeoJSONSource&); + GeoJSONSource(mbgl::style::GeoJSONSource&); ~GeoJSONSource(); diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp new file mode 100644 index 0000000000..d46b367c53 --- /dev/null +++ b/platform/android/src/style/sources/image_source.cpp @@ -0,0 +1,72 @@ +#include "image_source.hpp" + +// Java -> C++ conversion +#include "../android_conversion.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/premultiply.hpp> + +#include "../../bitmap.hpp" +#include <string> +#include <array> + +namespace mbgl { +namespace android { + + ImageSource::ImageSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<LatLngQuad> coordinatesObject) + : Source(env, std::make_unique<mbgl::style::ImageSource>( + jni::Make<std::string>(env, sourceId), + LatLngQuad::getLatLngArray(env, coordinatesObject) + ) + ) { + } + + ImageSource::ImageSource(mbgl::style::ImageSource& coreSource) + : Source(coreSource) { + } + + ImageSource::~ImageSource() = default; + + void ImageSource::setURL(jni::JNIEnv& env, jni::String url) { + // Update the core source + source.as<mbgl::style::ImageSource>()->ImageSource::setURL(jni::Make<std::string>(env, url)); + } + + jni::String ImageSource::getURL(jni::JNIEnv& env) { + optional<std::string> url = source.as<mbgl::style::ImageSource>()->ImageSource::getURL(); + return url ? jni::Make<jni::String>(env, *url) : jni::String(); + } + + void ImageSource::setImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) { + source.as<mbgl::style::ImageSource>()->setImage(Bitmap::GetImage(env, bitmap)); + } + + jni::Class<ImageSource> ImageSource::javaClass; + + jni::jobject* ImageSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = ImageSource::javaClass.template GetConstructor<jni::jlong>(env); + return ImageSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void ImageSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + ImageSource::javaClass = *jni::Class<ImageSource>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<ImageSource>( + env, ImageSource::javaClass, "nativePtr", + std::make_unique<ImageSource, JNIEnv&, jni::String, jni::Object<LatLngQuad>>, + "initialize", + "finalize", + METHOD(&ImageSource::setURL, "nativeSetUrl"), + METHOD(&ImageSource::getURL, "nativeGetUrl"), + METHOD(&ImageSource::setImage, "nativeSetImage") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp new file mode 100644 index 0000000000..9787a7294f --- /dev/null +++ b/platform/android/src/style/sources/image_source.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "source.hpp" +#include "../../geometry/lat_lng_quad.hpp" +#include <mbgl/style/sources/image_source.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class Bitmap; + +class ImageSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/ImageSource"; }; + + static jni::Class<ImageSource> javaClass; + + static void registerNative(jni::JNIEnv&); + + ImageSource(jni::JNIEnv&, jni::String, jni::Object<LatLngQuad>); + + ImageSource(mbgl::style::ImageSource&); + + ~ImageSource(); + + void setURL(jni::JNIEnv&, jni::String); + jni::String getURL(jni::JNIEnv&); + + void setImage(jni::JNIEnv&, jni::Object<Bitmap>); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class ImageSource + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp index 32fdb163b0..d45342a1ad 100644 --- a/platform/android/src/style/sources/raster_source.cpp +++ b/platform/android/src/style/sources/raster_source.cpp @@ -22,8 +22,8 @@ namespace android { ) { } - RasterSource::RasterSource(mbgl::Map& map, mbgl::style::RasterSource& coreSource) - : Source(map, coreSource) { + RasterSource::RasterSource(mbgl::style::RasterSource& coreSource) + : Source(coreSource) { } RasterSource::~RasterSource() = default; diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp index a79ccc10a4..84c49d7381 100644 --- a/platform/android/src/style/sources/raster_source.hpp +++ b/platform/android/src/style/sources/raster_source.hpp @@ -18,7 +18,7 @@ public: RasterSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint); - RasterSource(mbgl::Map&, mbgl::style::RasterSource&); + RasterSource(mbgl::style::RasterSource&); ~RasterSource(); diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp index e0e9bb9870..447b13019d 100644 --- a/platform/android/src/style/sources/source.cpp +++ b/platform/android/src/style/sources/source.cpp @@ -3,6 +3,7 @@ #include <jni/jni.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/util/logging.hpp> // Java -> C++ conversion @@ -25,11 +26,11 @@ namespace android { , source(*ownedSource) { } - Source::Source(mbgl::Map& coreMap, mbgl::style::Source& coreSource) : source(coreSource) , map(&coreMap) { + Source::Source(mbgl::style::Source& coreSource) + : source(coreSource) { } - Source::~Source() { - } + Source::~Source() = default; style::Source& Source::get() { return source; @@ -55,10 +56,11 @@ namespace android { } // Add source to map - _map.addSource(releaseCoreSource()); + _map.getStyle().addSource(releaseCoreSource()); + } - // Save pointer to the map - this->map = &_map; + void Source::setRendererFrontend(AndroidRendererFrontend& frontend_) { + rendererFrontend = &frontend_; } std::unique_ptr<mbgl::style::Source> Source::releaseCoreSource() { diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp index 49fc50d754..383017b66f 100644 --- a/platform/android/src/style/sources/source.hpp +++ b/platform/android/src/style/sources/source.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/source.hpp> #include "../value.hpp" +#include "../../android_renderer_frontend.hpp" #include <jni/jni.hpp> @@ -23,7 +24,7 @@ public: /* * Called when a Java object is created on the c++ side */ - Source(mbgl::Map&, mbgl::style::Source&); + Source(mbgl::style::Source&); /* * Called when a Java object was created from the jvm side @@ -41,6 +42,8 @@ public: void addToMap(mbgl::Map&); + void setRendererFrontend(AndroidRendererFrontend&); + virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0; jni::String getId(jni::JNIEnv&); @@ -57,8 +60,9 @@ protected: // Raw pointer that is valid until the source is removed from the map mbgl::style::Source& source; - // Map pointer is valid for newly created sources only after adding to the map - mbgl::Map* map; + // RendererFrontend pointer is valid only when + // added to the map + AndroidRendererFrontend* rendererFrontend; }; } // namespace android diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp index b4e70202b4..9ab3ca8e84 100644 --- a/platform/android/src/style/sources/sources.cpp +++ b/platform/android/src/style/sources/sources.cpp @@ -2,35 +2,46 @@ #include <mbgl/style/source.hpp> #include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/sources/image_source.hpp> #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include "source.hpp" #include "geojson_source.hpp" +#include "image_source.hpp" #include "raster_source.hpp" #include "unknown_source.hpp" #include "vector_source.hpp" +namespace { + + using namespace mbgl::android; + + Source* initializeSourcePeer(mbgl::style::Source& coreSource) { + Source* source; + if (coreSource.is<mbgl::style::VectorSource>()) { + source = new VectorSource(*coreSource.as<mbgl::style::VectorSource>()); + } else if (coreSource.is<mbgl::style::RasterSource>()) { + source = new RasterSource(*coreSource.as<mbgl::style::RasterSource>()); + } else if (coreSource.is<mbgl::style::GeoJSONSource>()) { + source = new GeoJSONSource(*coreSource.as<mbgl::style::GeoJSONSource>()); + } else if (coreSource.is<mbgl::style::ImageSource>()) { + source = new ImageSource(*coreSource.as<mbgl::style::ImageSource>()); + } else { + source = new UnknownSource(coreSource); + } + + return source; + } +} // namespace + namespace mbgl { namespace android { -Source* initializeSourcePeer(mbgl::Map& map, mbgl::style::Source& coreSource) { - Source* source; - if (coreSource.is<mbgl::style::VectorSource>()) { - source = new VectorSource(map, *coreSource.as<mbgl::style::VectorSource>()); - } else if (coreSource.is<mbgl::style::RasterSource>()) { - source = new RasterSource(map, *coreSource.as<mbgl::style::RasterSource>()); - } else if (coreSource.is<mbgl::style::GeoJSONSource>()) { - source = new GeoJSONSource(map, *coreSource.as<mbgl::style::GeoJSONSource>()); - } else { - source = new UnknownSource(map, coreSource); - } - - return source; -} -jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Source& coreSource) { - std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(map, coreSource)); +jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, AndroidRendererFrontend& frontend, mbgl::style::Source& coreSource) { + std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(coreSource)); + peerSource->setRendererFrontend(frontend); jni::jobject* result = peerSource->createJavaPeer(env); peerSource.release(); return result; @@ -39,6 +50,7 @@ jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style void registerNativeSources(jni::JNIEnv& env) { Source::registerNative(env); GeoJSONSource::registerNative(env); + ImageSource::registerNative(env); RasterSource::registerNative(env); UnknownSource::registerNative(env); VectorSource::registerNative(env); diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp index 09a8b35067..c7b36818b2 100644 --- a/platform/android/src/style/sources/sources.hpp +++ b/platform/android/src/style/sources/sources.hpp @@ -1,20 +1,18 @@ #pragma once -#include <mbgl/map/map.hpp> #include <mbgl/style/source.hpp> #include "source.hpp" +#include "../../android_renderer_frontend.hpp" #include <jni/jni.hpp> namespace mbgl { namespace android { - mbgl::android::Source* initializeSourcePeer(mbgl::Map&, mbgl::style::Source&); - - jni::jobject* createJavaSourcePeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Source&); + jni::jobject* createJavaSourcePeer(jni::JNIEnv&, AndroidRendererFrontend&, mbgl::style::Source&); void registerNativeSources(jni::JNIEnv&); -} -} +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp index 86736597c3..79f27bdfbf 100644 --- a/platform/android/src/style/sources/unknown_source.cpp +++ b/platform/android/src/style/sources/unknown_source.cpp @@ -12,8 +12,8 @@ namespace { namespace mbgl { namespace android { - UnknownSource::UnknownSource(mbgl::Map& map, mbgl::style::Source& coreSource) - : Source(map, coreSource) { + UnknownSource::UnknownSource(mbgl::style::Source& coreSource) + : Source(coreSource) { } jni::Class<UnknownSource> UnknownSource::javaClass; diff --git a/platform/android/src/style/sources/unknown_source.hpp b/platform/android/src/style/sources/unknown_source.hpp index 3c37239792..4a003c9a7f 100644 --- a/platform/android/src/style/sources/unknown_source.hpp +++ b/platform/android/src/style/sources/unknown_source.hpp @@ -16,7 +16,7 @@ public: static void registerNative(jni::JNIEnv&); - UnknownSource(mbgl::Map&, mbgl::style::Source&); + UnknownSource(mbgl::style::Source&); ~UnknownSource() = default; diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp index e2d9f60dec..7fe45441bd 100644 --- a/platform/android/src/style/sources/vector_source.cpp +++ b/platform/android/src/style/sources/vector_source.cpp @@ -1,5 +1,7 @@ #include "vector_source.hpp" +#include <mbgl/renderer/query.hpp> + // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" @@ -28,8 +30,8 @@ namespace android { ) { } - VectorSource::VectorSource(mbgl::Map& map, mbgl::style::VectorSource& coreSource) - : Source(map, coreSource) { + VectorSource::VectorSource(mbgl::style::VectorSource& coreSource) + : Source(coreSource) { } VectorSource::~VectorSource() = default; @@ -46,8 +48,8 @@ namespace android { using namespace mbgl::android::geojson; std::vector<mbgl::Feature> features; - if (map) { - features = map->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) }); + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) }); } return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features); } diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp index 643b468338..509fe068d1 100644 --- a/platform/android/src/style/sources/vector_source.hpp +++ b/platform/android/src/style/sources/vector_source.hpp @@ -19,7 +19,7 @@ public: VectorSource(jni::JNIEnv&, jni::String, jni::Object<>); - VectorSource(mbgl::Map&, mbgl::style::VectorSource&); + VectorSource(mbgl::style::VectorSource&); ~VectorSource(); |