Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
356 lines
15 KiB
C++
356 lines
15 KiB
C++
#include "map_snapshotter.hpp"
|
|
|
|
#include <mbgl/actor/scheduler.hpp>
|
|
#include <mbgl/renderer/renderer.hpp>
|
|
#include <mbgl/style/style.hpp>
|
|
#include <mbgl/util/logging.hpp>
|
|
#include <mbgl/util/string.hpp>
|
|
|
|
#include "attach_env.hpp"
|
|
#include "../style/layers/layer_manager.hpp"
|
|
#include "map_snapshot.hpp"
|
|
|
|
namespace mbgl {
|
|
namespace android {
|
|
|
|
MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
|
|
const jni::Object<MapSnapshotter>& _obj,
|
|
const jni::Object<FileSource>& _jFileSource,
|
|
jni::jfloat _pixelRatio,
|
|
jni::jint width,
|
|
jni::jint height,
|
|
const jni::String& styleURL,
|
|
const jni::String& styleJSON,
|
|
const jni::Object<LatLngBounds>& region,
|
|
const jni::Object<CameraPosition>& 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<uint32_t>(width), static_cast<uint32_t>(height)};
|
|
|
|
showLogo = _showLogo;
|
|
|
|
// Create the core snapshotter
|
|
snapshotter = std::make_unique<mbgl::MapSnapshotter>(
|
|
size,
|
|
pixelRatio,
|
|
mbgl::android::FileSource::getSharedResourceOptions(_env, _jFileSource),
|
|
mbgl::android::FileSource::getSharedClientOptions(_env, _jFileSource),
|
|
*this,
|
|
_localIdeographFontFamily ? jni::Make<std::string>(_env, _localIdeographFontFamily)
|
|
: std::optional<std::string>{});
|
|
|
|
if (position) {
|
|
snapshotter->setCameraOptions(CameraPosition::getCameraOptions(_env, position, pixelRatio));
|
|
}
|
|
|
|
if (region) {
|
|
snapshotter->setRegion(LatLngBounds::getLatLngBounds(_env, region));
|
|
}
|
|
|
|
if (styleJSON) {
|
|
snapshotter->setStyleJSON(jni::Make<std::string>(_env, styleJSON));
|
|
} else {
|
|
snapshotter->setStyleURL(jni::Make<std::string>(_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;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::start(JNIEnv& env) {
|
|
MBGL_VERIFY_THREAD(tid);
|
|
activateFilesource(env);
|
|
snapshotter->snapshot([this](std::exception_ptr err,
|
|
PremultipliedImage image,
|
|
std::vector<std::string> attributions,
|
|
mbgl::MapSnapshotter::PointForFn pointForFn,
|
|
mbgl::MapSnapshotter::LatLngForFn latLngForFn) {
|
|
MBGL_VERIFY_THREAD(tid);
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<MapSnapshotter>::Singleton(*_env);
|
|
|
|
if (err) {
|
|
// error handler callback
|
|
static auto onSnapshotFailed = javaClass.GetMethod<void(jni::String)>(*_env, "onSnapshotFailed");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onSnapshotFailed, jni::Make<jni::String>(*_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<void(jni::Object<MapSnapshot>)>(*_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<std::string>(env, styleURL));
|
|
}
|
|
|
|
void MapSnapshotter::setStyleJson(JNIEnv& env, const jni::String& styleJSON) {
|
|
snapshotter->setStyleJSON(jni::Make<std::string>(env, styleJSON));
|
|
}
|
|
|
|
void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) {
|
|
auto size = mbgl::Size{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
|
|
snapshotter->setSize(size);
|
|
}
|
|
|
|
void MapSnapshotter::setCameraPosition(JNIEnv& env, const jni::Object<CameraPosition>& position) {
|
|
auto options = CameraPosition::getCameraOptions(env, position, pixelRatio);
|
|
snapshotter->setCameraOptions(options);
|
|
}
|
|
|
|
void MapSnapshotter::setRegion(JNIEnv& env, const jni::Object<LatLngBounds>& 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<MapSnapshotter>::Singleton(*_env);
|
|
static auto onDidFailLoadingStyle = javaClass.GetMethod<void(jni::String)>(*_env, "onDidFailLoadingStyle");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidFailLoadingStyle, jni::Make<jni::String>(*_env, error));
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::onDidFinishLoadingStyle() {
|
|
MBGL_VERIFY_THREAD(tid);
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
|
|
static auto& javaClass = jni::Class<MapSnapshotter>::Singleton(*_env);
|
|
static auto onDidFinishLoadingStyle = javaClass.GetMethod<void()>(*_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<MapSnapshotter>::Singleton(*_env);
|
|
static auto onStyleImageMissing = javaClass.GetMethod<void(jni::String)>(*_env, "onStyleImageMissing");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onStyleImageMissing, jni::Make<jni::String>(*_env, imageName));
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) {
|
|
assert(nativeLayerPtr != 0);
|
|
const auto layers = snapshotter->getStyle().getLayers();
|
|
auto* layer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
// Check index
|
|
const int numLayers = layers.size() - 1;
|
|
if (index > numLayers || index < 0) {
|
|
Log::Error(Event::JNI, "Index out of range: " + std::to_string(index));
|
|
jni::ThrowNew(env,
|
|
jni::FindClass(env, "org/maplibre/android/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, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::addLayerBelow(JNIEnv& env, jlong nativeLayerPtr, const jni::String& below) {
|
|
assert(nativeLayerPtr != 0);
|
|
|
|
auto* layer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
try {
|
|
layer->addToStyle(
|
|
snapshotter->getStyle(),
|
|
below ? std::optional<std::string>(jni::Make<std::string>(env, below)) : std::optional<std::string>());
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, const jni::String& above) {
|
|
assert(nativeLayerPtr != 0);
|
|
auto* newLayer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
|
|
// Find the sibling
|
|
const auto snapshotterLayers = snapshotter->getStyle().getLayers();
|
|
auto siblingId = jni::Make<std::string>(env, above);
|
|
|
|
size_t index = 0;
|
|
for (auto* snapshotterLayer : snapshotterLayers) {
|
|
++index;
|
|
if (snapshotterLayer->getID() == siblingId) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if we found a sibling to place before
|
|
std::optional<std::string> before;
|
|
if (index > snapshotterLayers.size()) {
|
|
// Not found
|
|
jni::ThrowNew(env,
|
|
jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"),
|
|
std::string("Could not find layer: ").append(siblingId).c_str());
|
|
} else if (index < snapshotterLayers.size()) {
|
|
// Place before the sibling
|
|
before = {snapshotterLayers.at(index)->getID()};
|
|
}
|
|
|
|
// Add the layer
|
|
try {
|
|
newLayer->addToStyle(snapshotter->getStyle(), before);
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::addSource(JNIEnv& env, const jni::Object<Source>& obj, jlong sourcePtr) {
|
|
assert(sourcePtr != 0);
|
|
|
|
auto* source = reinterpret_cast<Source*>(sourcePtr);
|
|
try {
|
|
source->addToStyle(env, obj, snapshotter->getStyle());
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/sources/CannotAddSourceException"), error.what());
|
|
}
|
|
}
|
|
|
|
void MapSnapshotter::addImages(JNIEnv& env, const jni::Array<jni::Object<mbgl::android::Image>>& 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<mbgl::style::Image>(image));
|
|
}
|
|
}
|
|
|
|
jni::Local<jni::Object<Layer>> MapSnapshotter::getLayer(JNIEnv& env, const jni::String& layerId) {
|
|
// Find the layer
|
|
mbgl::style::Layer* coreLayer = snapshotter->getStyle().getLayer(jni::Make<std::string>(env, layerId));
|
|
if (!coreLayer) {
|
|
mbgl::Log::Debug(mbgl::Event::JNI, "No layer found");
|
|
return jni::Local<jni::Object<Layer>>();
|
|
}
|
|
|
|
// Create and return the layer's native peer
|
|
return LayerManagerAndroid::get()->createJavaLayerPeer(env, *coreLayer);
|
|
}
|
|
|
|
jni::Local<jni::Object<Source>> MapSnapshotter::getSource(JNIEnv& env, const jni::String& sourceId) {
|
|
// Find the source
|
|
mbgl::style::Source* coreSource = snapshotter->getStyle().getSource(jni::Make<std::string>(env, sourceId));
|
|
if (!coreSource) {
|
|
mbgl::Log::Debug(mbgl::Event::JNI, "No source found");
|
|
return jni::Local<jni::Object<Source>>();
|
|
}
|
|
|
|
// Create and return the source's native peer
|
|
return jni::NewLocal(env, Source::peerForCoreSource(env, *coreSource));
|
|
}
|
|
|
|
// Static methods //
|
|
|
|
void MapSnapshotter::registerNative(jni::JNIEnv& env) {
|
|
// Lookup the class
|
|
static auto& javaClass = jni::Class<MapSnapshotter>::Singleton(env);
|
|
|
|
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
|
|
|
// Register the peer
|
|
jni::RegisterNativePeer<MapSnapshotter>(env,
|
|
javaClass,
|
|
"nativePtr",
|
|
jni::MakePeer<MapSnapshotter,
|
|
const jni::Object<MapSnapshotter>&,
|
|
const jni::Object<FileSource>&,
|
|
jni::jfloat,
|
|
jni::jint,
|
|
jni::jint,
|
|
const jni::String&,
|
|
const jni::String&,
|
|
const jni::Object<LatLngBounds>&,
|
|
const jni::Object<CameraPosition>&,
|
|
jni::jboolean,
|
|
const jni::String&>,
|
|
"nativeInitialize",
|
|
"finalize",
|
|
METHOD(&MapSnapshotter::setStyleUrl, "setStyleUrl"),
|
|
METHOD(&MapSnapshotter::addLayerAt, "nativeAddLayerAt"),
|
|
METHOD(&MapSnapshotter::addLayerBelow, "nativeAddLayerBelow"),
|
|
METHOD(&MapSnapshotter::addLayerAbove, "nativeAddLayerAbove"),
|
|
METHOD(&MapSnapshotter::addSource, "nativeAddSource"),
|
|
METHOD(&MapSnapshotter::addImages, "nativeAddImages"),
|
|
METHOD(&MapSnapshotter::getLayer, "nativeGetLayer"),
|
|
METHOD(&MapSnapshotter::getSource, "nativeGetSource"),
|
|
METHOD(&MapSnapshotter::setStyleJson, "setStyleJson"),
|
|
METHOD(&MapSnapshotter::setSize, "setSize"),
|
|
METHOD(&MapSnapshotter::setCameraPosition, "setCameraPosition"),
|
|
METHOD(&MapSnapshotter::setRegion, "setRegion"),
|
|
METHOD(&MapSnapshotter::start, "nativeStart"),
|
|
METHOD(&MapSnapshotter::cancel, "nativeCancel"));
|
|
}
|
|
|
|
} // namespace android
|
|
} // namespace mbgl
|