#include "map_snapshotter.hpp" #include #include #include #include #include #include "../attach_env.hpp" #include "map_snapshot.hpp" namespace mbgl { namespace android { MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env, const jni::Object& _obj, const jni::Object& _jFileSource, jni::jfloat _pixelRatio, jni::jint width, jni::jint height, const jni::String& styleURL, const jni::String& styleJSON, const jni::Object& region, const jni::Object& position, jni::jboolean _showLogo, const jni::String& _localIdeographFontFamily) : javaPeer(_env, _obj) , pixelRatio(_pixelRatio) { // Get a reference to the JavaVM for callbacks if (_env.GetJavaVM(&vm) < 0) { _env.ExceptionDescribe(); return; } weakScheduler = mbgl::Scheduler::GetCurrent()->makeWeakPtr(); jFileSource = FileSource::getNativePeer(_env, _jFileSource); auto size = mbgl::Size { static_cast(width), static_cast(height) }; showLogo = _showLogo; // Create the core snapshotter snapshotter = std::make_unique( size, pixelRatio, mbgl::android::FileSource::getSharedResourceOptions(_env, _jFileSource), *this, _localIdeographFontFamily ? jni::Make(_env, _localIdeographFontFamily) : optional{}); if (position) { snapshotter->setCameraOptions(CameraPosition::getCameraOptions(_env, position, pixelRatio)); } if (region) { snapshotter->setRegion(LatLngBounds::getLatLngBounds(_env, region)); } if (styleJSON) { snapshotter->setStyleJSON(jni::Make(_env, styleJSON)); } else { snapshotter->setStyleURL(jni::Make(_env, styleURL)); } } MapSnapshotter::~MapSnapshotter() { auto guard = weakScheduler.lock(); if (weakScheduler && weakScheduler.get() != mbgl::Scheduler::GetCurrent()) { snapshotter->cancel(); weakScheduler->schedule([ptr = snapshotter.release()]() mutable { if (ptr) { delete ptr; ptr = nullptr; } }); } } void MapSnapshotter::start(JNIEnv& env) { MBGL_VERIFY_THREAD(tid); activateFilesource(env); snapshotter->snapshot([this](std::exception_ptr err, PremultipliedImage image, std::vector attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn) { MBGL_VERIFY_THREAD(tid); android::UniqueEnv _env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*_env); if (err) { // error handler callback static auto onSnapshotFailed = javaClass.GetMethod(*_env, "onSnapshotFailed"); auto weakReference = javaPeer.get(*_env); if (weakReference) { weakReference.Call(*_env, onSnapshotFailed, jni::Make(*_env, util::toString(err))); } } else { // Create the wrapper auto mapSnapshot = android::MapSnapshot::New(*_env, std::move(image), pixelRatio, attributions, showLogo, pointForFn, latLngForFn); // invoke callback static auto onSnapshotReady = javaClass.GetMethod)>(*_env, "onSnapshotReady"); auto weakReference = javaPeer.get(*_env); if (weakReference) { weakReference.Call(*_env, onSnapshotReady, mapSnapshot); } } deactivateFilesource(*_env); }); } void MapSnapshotter::cancel(JNIEnv& env) { MBGL_VERIFY_THREAD(tid); snapshotter->cancel(); deactivateFilesource(env); } void MapSnapshotter::setStyleUrl(JNIEnv& env, const jni::String& styleURL) { snapshotter->setStyleURL(jni::Make(env, styleURL)); } void MapSnapshotter::setStyleJson(JNIEnv& env, const jni::String& styleJSON) { snapshotter->setStyleJSON(jni::Make(env, styleJSON)); } void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) { auto size = mbgl::Size { static_cast(width), static_cast(height) }; snapshotter->setSize(size); } void MapSnapshotter::setCameraPosition(JNIEnv& env, const jni::Object& position) { auto options = CameraPosition::getCameraOptions(env, position, pixelRatio); snapshotter->setCameraOptions(options); } void MapSnapshotter::setRegion(JNIEnv& env, const jni::Object& region) { snapshotter->setRegion(LatLngBounds::getLatLngBounds(env, region)); } // Private methods // void MapSnapshotter::activateFilesource(JNIEnv& env) { if (!activatedFilesource) { activatedFilesource = true; jFileSource->resume(env); } } void MapSnapshotter::deactivateFilesource(JNIEnv& env) { if (activatedFilesource) { activatedFilesource = false; jFileSource->pause(env); } } void MapSnapshotter::onDidFailLoadingStyle(const std::string& error) { MBGL_VERIFY_THREAD(tid); android::UniqueEnv _env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*_env); static auto onDidFailLoadingStyle = javaClass.GetMethod(*_env, "onDidFailLoadingStyle"); auto weakReference = javaPeer.get(*_env); if (weakReference) { weakReference.Call(*_env, onDidFailLoadingStyle, jni::Make(*_env, error)); } } void MapSnapshotter::onDidFinishLoadingStyle() { MBGL_VERIFY_THREAD(tid); android::UniqueEnv _env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*_env); static auto onDidFinishLoadingStyle = javaClass.GetMethod(*_env, "onDidFinishLoadingStyle"); auto weakReference = javaPeer.get(*_env); if (weakReference) { weakReference.Call(*_env, onDidFinishLoadingStyle); } } void MapSnapshotter::onStyleImageMissing(const std::string& imageName) { MBGL_VERIFY_THREAD(tid); android::UniqueEnv _env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*_env); static auto onStyleImageMissing = javaClass.GetMethod(*_env, "onStyleImageMissing"); auto weakReference = javaPeer.get(*_env); if (weakReference) { weakReference.Call(*_env, onStyleImageMissing, jni::Make(*_env, imageName)); } } void MapSnapshotter::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) { assert(nativeLayerPtr != 0); auto layers = snapshotter->getStyle().getLayers(); auto* layer = reinterpret_cast(nativeLayerPtr); // Check index int numLayers = layers.size() - 1; if (index > numLayers || index < 0) { Log::Error(Event::JNI, "Index out of range: %i", index); jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), std::string("Invalid index").c_str()); } // Insert it below the current at that index try { layer->addToStyle(snapshotter->getStyle(), layers.at(index)->getID()); } catch (const std::runtime_error& error) { jni::ThrowNew( env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); } } void MapSnapshotter::addLayerBelow(JNIEnv& env, jlong nativeLayerPtr, const jni::String& below) { assert(nativeLayerPtr != 0); auto* layer = reinterpret_cast(nativeLayerPtr); try { layer->addToStyle( snapshotter->getStyle(), below ? mbgl::optional(jni::Make(env, below)) : mbgl::optional()); } catch (const std::runtime_error& error) { jni::ThrowNew( env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); } } void MapSnapshotter::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, const jni::String& above) { assert(nativeLayerPtr != 0); auto* layer = reinterpret_cast(nativeLayerPtr); // Find the sibling auto layers = snapshotter->getStyle().getLayers(); auto siblingId = jni::Make(env, above); size_t index = 0; for (auto l : layers) { if (l->getID() == siblingId) { break; } index++; } // Check if we found a sibling to place before mbgl::optional before; if (index + 1 > layers.size()) { // Not found jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), std::string("Could not find layer: ").append(siblingId).c_str()); } else if (index + 1 < layers.size()) { // Place before the sibling before = {layers.at(index + 1)->getID()}; } // Add the layer try { layer->addToStyle(snapshotter->getStyle(), before); } catch (const std::runtime_error& error) { jni::ThrowNew( env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); } } void MapSnapshotter::addSource(JNIEnv& env, const jni::Object& obj, jlong sourcePtr) { assert(sourcePtr != 0); auto* source = reinterpret_cast(sourcePtr); try { source->addToStyle(env, obj, snapshotter->getStyle()); } catch (const std::runtime_error& error) { jni::ThrowNew( env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); } } void MapSnapshotter::addImages(JNIEnv& env, const jni::Array>& jimages) { jni::NullCheck(env, &jimages); std::size_t len = jimages.Length(env); for (std::size_t i = 0; i < len; i++) { auto image = mbgl::android::Image::getImage(env, jimages.Get(env, i)); snapshotter->getStyle().addImage(std::make_unique(image)); } } // Static methods // void MapSnapshotter::registerNative(jni::JNIEnv& env) { // Lookup the class static auto& javaClass = jni::Class::Singleton(env); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) // Register the peer jni::RegisterNativePeer(env, javaClass, "nativePtr", jni::MakePeer&, const jni::Object&, jni::jfloat, jni::jint, jni::jint, const jni::String&, const jni::String&, const jni::Object&, const jni::Object&, jni::jboolean, const jni::String&>, "nativeInitialize", "finalize", METHOD(&MapSnapshotter::setStyleUrl, "nativeSetStyleUrl"), METHOD(&MapSnapshotter::addLayerAt, "nativeAddLayerAt"), METHOD(&MapSnapshotter::addLayerBelow, "nativeAddLayerBelow"), METHOD(&MapSnapshotter::addLayerAbove, "nativeAddLayerAbove"), METHOD(&MapSnapshotter::addSource, "nativeAddSource"), METHOD(&MapSnapshotter::addImages, "nativeAddImages"), METHOD(&MapSnapshotter::setStyleJson, "nativeSetStyleJson"), METHOD(&MapSnapshotter::setSize, "setSize"), METHOD(&MapSnapshotter::setCameraPosition, "setCameraPosition"), METHOD(&MapSnapshotter::setRegion, "setRegion"), METHOD(&MapSnapshotter::start, "nativeStart"), METHOD(&MapSnapshotter::cancel, "nativeCancel")); } } // namespace android } // namespace mbgl