diff options
author | Leith Bade <leith@leithalweapon.geek.nz> | 2014-11-05 22:53:48 +1100 |
---|---|---|
committer | Leith Bade <leith@leithalweapon.geek.nz> | 2014-11-05 22:53:48 +1100 |
commit | 9476b7a66f4864a1a10b083f6afe3bececd6fa84 (patch) | |
tree | f7229765f1da4112d2c8800c27aa1dd6ce23ea58 /android | |
parent | fcab29cab0abe0142770d817c64c0f711e7f7ae0 (diff) | |
download | qtlocation-mapboxgl-9476b7a66f4864a1a10b083f6afe3bececd6fa84.tar.gz |
Add JNI code to build system
Diffstat (limited to 'android')
-rw-r--r-- | android/JNI.cpp | 853 | ||||
-rw-r--r-- | android/NativeMapView.cpp | 528 | ||||
-rw-r--r-- | android/NativeMapView.hpp | 111 | ||||
-rw-r--r-- | android/log.h | 18 | ||||
-rw-r--r-- | android/mapboxgl-app.gyp | 42 |
5 files changed, 1552 insertions, 0 deletions
diff --git a/android/JNI.cpp b/android/JNI.cpp new file mode 100644 index 0000000000..2dbfedcbe2 --- /dev/null +++ b/android/JNI.cpp @@ -0,0 +1,853 @@ +#include <cstdint> +#include <cinttypes> + +#include <string> +#include <locale> +#include <codecvt> +#include <array> +#include <vector> + +#include <jni.h> + +#include <android/native_window_jni.h> + +#include "log.h" + +#include "NativeMapView.hpp" + +#pragma clang diagnostic ignored "-Wunused-parameter" + +namespace mbgl { +namespace android { + +jmethodID on_map_changed_id = nullptr; + +jclass lon_lat_class = nullptr; +jmethodID lon_lat_constructor_id = nullptr; +jfieldID lon_lat_lon_id = nullptr; +jfieldID lon_lat_lat_id = nullptr; + +jclass lon_lat_zoom_class = nullptr; +jmethodID lon_lat_zoom_constructor_id = nullptr; +jfieldID lon_lat_zoom_lon_id = nullptr; +jfieldID lon_lat_zoom_lat_id = nullptr; +jfieldID lon_lat_zoom_zoom_id = nullptr; + +jclass runtime_exception_class = nullptr; + +jmethodID set_to_array_id = nullptr; + +jclass tree_set_class = nullptr; +jmethodID tree_set_constructor_id = nullptr; +jmethodID tree_set_add_id = nullptr; + +bool throw_error(JNIEnv* env, const char* msg) { + if (env->ThrowNew(runtime_exception_class, msg) < 0) { + env->ExceptionDescribe(); + return false; + } + + return true; +} + +std::string std_string_from_jstring(JNIEnv* env, jstring jstr) { + std::string str; + jsize len = env->GetStringLength(jstr); + if (len < 0) { + env->ExceptionDescribe(); + return str; + } + const jchar* chars = env->GetStringChars(jstr, nullptr); + if (chars == nullptr) { + env->ExceptionDescribe(); + return str; + } + std::u16string ustr(reinterpret_cast<const char16_t*>(chars), len); + env->ReleaseStringChars(jstr, chars); + chars = nullptr; + str = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t>().to_bytes(ustr); + return str; +} + +jstring std_string_to_jstring(JNIEnv* env, std::string str) { + std::u16string ustr = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t>().from_bytes(str); + + jstring jstr = env->NewString(reinterpret_cast<const jchar*>(ustr.c_str()), ustr.size()); + if (jstr == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return jstr; +} + + +// TODO: change from Java TreeSet to ArrayList +std::vector<std::string> std_vector_string_from_jobject(JNIEnv* env, jobject jset) { + std::vector<std::string> vector; + + jobjectArray array = reinterpret_cast<jobjectArray>(env->CallObjectMethod(jset, set_to_array_id)); + if (env->ExceptionCheck() || (array == nullptr)) { + env->ExceptionDescribe(); + return vector; + } + + jsize len = env->GetArrayLength(array); + if (len < 0) { + env->ExceptionDescribe(); + return vector; + } + + for (jsize i = 0; i < len; i++) { + jstring jstr = reinterpret_cast<jstring>(env->GetObjectArrayElement(array, i)); + if (jstr == nullptr) { + env->ExceptionDescribe(); + return vector; + } + + vector.push_back(std_string_from_jstring(env, jstr)); + } + + return vector; +} + +// TODO: change from Java TreeSet to ArrayList +jobject std_vector_string_to_jobject(JNIEnv* env, std::vector<std::string> vector) { + jobject jset = env->NewObject(tree_set_class, tree_set_constructor_id); + if (jset == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + for (std::string str : vector) { + env->CallBooleanMethod(jset, tree_set_add_id, std_string_to_jstring(env, str)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return nullptr; + } + } + + return jset; +} + +} // namespace android +} // namespace mbgl + +namespace { + +using namespace mbgl::android; + +// TODO: wrap C++ exceptions? +// TODO: wrap other sorts of exceptions? eg coffee catch + +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring default_style_json) { + VERBOSE("nativeCreate"); + NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, default_style_json)); + if (native_map_view == nullptr) { + throw_error(env, "Unable to create NativeMapView."); + return 0; + } + jlong map_view_ptr = reinterpret_cast<jlong>(native_map_view); + return map_view_ptr; +} + +void JNICALL nativeDestroy(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeDestroy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + delete native_map_view; + native_map_view = nullptr; +} + +void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeInitializeDisplay"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + if (!native_map_view->initializeDisplay()) { + throw_error(env, "Unable to initialize GL display."); + } +} + +void JNICALL nativeTerminateDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeTerminateDisplay"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->terminateDisplay(); +} + +void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeInitializeContext"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + if (!native_map_view->initializeContext()) { + throw_error(env, "Unable to initialize GL context."); + } +} + +void JNICALL nativeTerminateContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeTerminateContext"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->terminateContext(); +} + +void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject surface) { + VERBOSE("nativeCreateSurface"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + if (!native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))) { + throw_error(env, "Unable to create GL surface."); + } +} + +void JNICALL nativeDestroySurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeDestroySurface"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->destroySurface(); +} + +void JNICALL nativeStart(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStart"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->start(); +} + +void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStop"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->stop(); +} + +void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeRerender"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->rerender(); +} + +void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeUpdate"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->update(); +} + +void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCleanup"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->cleanup(); +} + +// TODO remove these? +void JNICALL nativeAddDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeAddDefaultSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); +// native_map_view->getMap()->addDefaultSource(); +} + +void JNICALL nativeRemoveDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeRemoveDefaultSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); +// native_map_view->getMap()->removeDefaultSource(); +} + +void JNICALL nativeAddSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name, jstring url) { + VERBOSE("nativeAddSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); +// native_map_view->getMap()->addSource(std_string_from_jstring(env, name), std_string_from_jstring(env, url)); +} + +void JNICALL nativeRemoveSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name) { + VERBOSE("nativeRemoveSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); +// native_map_view->getMap()->removeSource(std_string_from_jstring(env, name)); +} + +void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { + VERBOSE("nativeResize"); + ASSERT(native_map_view_ptr != 0); + ASSERT(width >= 0); + ASSERT(height >= 0); + ASSERT(width <= UINT16_MAX); + ASSERT(height <= UINT16_MAX); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->resize(width, height, ratio); +} + +void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { + VERBOSE("nativeResize"); + ASSERT(native_map_view_ptr != 0); + ASSERT(width >= 0); + ASSERT(height >= 0); + ASSERT(width <= UINT16_MAX); + ASSERT(height <= UINT16_MAX); + ASSERT(fb_width >= 0); + ASSERT(fb_height >= 0); + ASSERT(fb_width <= UINT16_MAX); + ASSERT(fb_height <= UINT16_MAX); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->resize(width, height, ratio, fb_width, fb_height); +} + +void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject applied_classes) { + VERBOSE("nativeSetAppliedClasses"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, applied_classes)); +} + +jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetAppliedClasses"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return std_vector_string_to_jobject(env, native_map_view->getMap()->getAppliedClasses()); +} + +void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { + VERBOSE("nativeSetDefaultTransitionDuration"); + ASSERT(native_map_view_ptr != 0); + ASSERT(duration_milliseconds >= 0); + //ASSERT(duration_milliseconds <= UINT64_MAX); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setDefaultTransitionDuration(duration_milliseconds); +} + +void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json) { + VERBOSE("nativeSetStyleJSON"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json)); +} + +jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetStyleJSON"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return std_string_to_jstring(env, native_map_view->getMap()->getStyleJSON()); +} + +void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCancelTransitions"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->cancelTransitions(); +} + +void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { + VERBOSE("nativeMoveBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->moveBy(dx, dy, duration); +} + +void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { + VERBOSE("nativeSetLonLat"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + + double lon = env->GetDoubleField(lon_lat, lon_lat_lon_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double lat = env->GetDoubleField(lon_lat, lon_lat_lat_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + // TODO remove when fixed + DEBUG("lon lat %f %f", lon, lat); + native_map_view->getMap()->setLonLat(lon, lat, duration); +} + +jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetLonLat"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + double lon, lat; + native_map_view->getMap()->getLonLat(lon, lat); + + jobject ret = env->NewObject(lon_lat_class, lon_lat_constructor_id, lon, lat); + if (ret == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return ret; +} + +void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartPanning"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->startPanning(); +} + +void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopPanning"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->stopPanning(); +} + +void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetPosition"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->resetPosition(); +} + +void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { + VERBOSE("nativeScaleBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->scaleBy(ds, cx, cy, duration); +} + +void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { + VERBOSE("nativeSetScale"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setScale(scale, cx, cy, duration); +} + +jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetScale"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->getScale(); +} + +void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { + VERBOSE("nativeSetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setZoom(zoom, duration); +} + +jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->getZoom(); +} + +void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { + VERBOSE("nativeSetLonLatZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + + double lon = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lon_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double lat = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lat_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double zoom = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_zoom_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + native_map_view->getMap()->setLonLatZoom(lon, lat, zoom, duration); +} + +jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetLonLatZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + double lon, lat, zoom; + native_map_view->getMap()->getLonLatZoom(lon, lat, zoom); + + jobject ret = env->NewObject(lon_lat_zoom_class, lon_lat_zoom_constructor_id, lon, lat, zoom); + if (ret == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return ret; +} + +void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->resetZoom(); +} + +void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartScaling"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->startScaling(); +} + +void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopScaling"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->stopScaling(); +} + +jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetMinZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->getMinZoom(); +} + +jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetMaxZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->getMaxZoom(); +} + +void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { + VERBOSE("nativeRotateBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->rotateBy(sx, sy, ex, ey, duration); +} + +void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble duration) { + VERBOSE("nativeSetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + // TODO update java class + //native_map_view->getMap()->setAngle(angle, duration); + double degrees = -angle / M_PI * 180; + native_map_view->getMap()->setBearing(degrees, duration); +} + +void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble cx, jdouble cy) { + VERBOSE("nativeSetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + // TODO update java class + //native_map_view->getMap()->setAngle(angle, cx, cy); + double degrees = -angle / M_PI * 180; + native_map_view->getMap()->setBearing(degrees, cx, cy); +} + +jdouble JNICALL nativeGetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + // TODO update java class + //return native_map_view->getMap()->getAngle(); + double degrees = native_map_view->getMap()->getBearing(); + return -degrees * M_PI / 180; +} + +void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetNorth"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->resetNorth(); +} + +void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartRotating"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->startRotating(); +} + +void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopRotating"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->stopRotating(); +} + +jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCanRotate"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->canRotate(); +} + +void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { + VERBOSE("nativeSetDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->setDebug(debug); +} + +void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeToggleDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + native_map_view->getMap()->toggleDebug(); +} + +jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr); + return native_map_view->getMap()->getDebug(); +} + +} // namespace + +extern "C" { + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + VERBOSE("JNI_OnLoad"); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return JNI_ERR; + } + + lon_lat_class = env->FindClass("com/mapbox/mapboxgl/lib/LonLat"); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_constructor_id = env->GetMethodID(lon_lat_class, "<init>", "(DD)V"); + if (lon_lat_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_lon_id = env->GetFieldID(lon_lat_class, "lon", "D"); + if (lon_lat_lon_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_lat_id = env->GetFieldID(lon_lat_class, "lat", "D"); + if (lon_lat_lat_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_class = env->FindClass("com/mapbox/mapboxgl/lib/LonLatZoom"); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_constructor_id = env->GetMethodID(lon_lat_zoom_class, "<init>", "(DDD)V"); + if (lon_lat_zoom_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_lon_id = env->GetFieldID(lon_lat_zoom_class, "lon", "D"); + if (lon_lat_zoom_lon_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_lat_id = env->GetFieldID(lon_lat_zoom_class, "lat", "D"); + if (lon_lat_zoom_lat_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_zoom_id = env->GetFieldID(lon_lat_zoom_class, "zoom", "D"); + if (lon_lat_zoom_zoom_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + jclass native_map_view_class = env->FindClass("com/mapbox/mapboxgl/lib/NativeMapView"); + if (native_map_view_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + on_map_changed_id = env->GetMethodID(native_map_view_class, "onMapChanged", "()V"); + if (on_map_changed_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + runtime_exception_class = env->FindClass("java/lang/RuntimeException"); + if (runtime_exception_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + jclass set_class = env->FindClass("java/util/Set"); + if (set_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + set_to_array_id = env->GetMethodID(set_class, "toArray", "()[Ljava/lang/Object;"); + if (set_to_array_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_class = env->FindClass("java/util/TreeSet"); + if (tree_set_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_constructor_id = env->GetMethodID(tree_set_class, "<init>", "()V"); + if (tree_set_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_add_id = env->GetMethodID(tree_set_class, "add", "(Ljava/lang/Object;)Z"); + if (tree_set_add_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + std::array<JNINativeMethod, 54> methods = {{ // Can remove the extra brace in C++14 + { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast<void*>(&nativeCreate) }, + { "nativeDestroy", "(J)V", reinterpret_cast<void*>(&nativeDestroy) }, + { "nativeInitializeDisplay", "(J)V", reinterpret_cast<void*>(&nativeInitializeDisplay) }, + { "nativeTerminateDisplay", "(J)V", reinterpret_cast<void*>(&nativeTerminateDisplay) }, + { "nativeInitializeContext", "(J)V", reinterpret_cast<void*>(&nativeInitializeContext) }, + { "nativeTerminateContext", "(J)V", reinterpret_cast<void*>(&nativeTerminateContext) }, + { "nativeCreateSurface", "(JLandroid/view/Surface;)V", reinterpret_cast<void*>(&nativeCreateSurface) }, + { "nativeDestroySurface", "(J)V", reinterpret_cast<void*>(&nativeDestroySurface) }, + { "nativeStart", "(J)V", reinterpret_cast<void*>(&nativeStart) }, + { "nativeStop", "(J)V", reinterpret_cast<void*>(&nativeStop) }, + { "nativeRerender", "(J)V", reinterpret_cast<void*>(&nativeRerender) }, + { "nativeUpdate", "(J)V", reinterpret_cast<void*>(&nativeUpdate) }, + { "nativeCleanup", "(J)V", reinterpret_cast<void*>(&nativeCleanup) }, + { "nativeAddDefaultSource", "(J)V", reinterpret_cast<void*>(&nativeAddDefaultSource) }, + { "nativeRemoveDefaultSource", "(J)V", reinterpret_cast<void*>(&nativeRemoveDefaultSource) }, + { "nativeAddSource", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(&nativeAddSource) }, + { "nativeRemoveSource", "(JLjava/lang/String;)V", reinterpret_cast<void*>(&nativeRemoveSource) }, + { "nativeResize", "(JIIF)V", reinterpret_cast<void*>(static_cast<void JNICALL(*)(JNIEnv*,jobject,jlong,jint,jint,jfloat)>(&nativeResize)) }, + { "nativeResize", "(JIIFII)V", reinterpret_cast<void*>(static_cast<void JNICALL(*)(JNIEnv*,jobject,jlong,jint,jint,jfloat,jint,jint)>(&nativeResize)) }, + { "nativeSetAppliedClasses", "(JLjava/util/Set;)V", reinterpret_cast<void*>(&nativeSetAppliedClasses) }, + { "nativeGetAppliedClasses", "(J)Ljava/util/Set;", reinterpret_cast<void*>(&nativeGetAppliedClasses) }, + { "nativeSetDefaultTransitionDuration", "(JJ)V", reinterpret_cast<void*>(&nativeSetDefaultTransitionDuration) }, + { "nativeSetStyleJSON", "(JLjava/lang/String;)V", reinterpret_cast<void*>(&nativeSetStyleJSON) }, + { "nativeGetStyleJSON", "(J)Ljava/lang/String;", reinterpret_cast<void*>(&nativeGetStyleJSON) }, + { "nativeCancelTransitions", "(J)V", reinterpret_cast<void*>(&nativeCancelTransitions) }, + { "nativeMoveBy", "(JDDD)V", reinterpret_cast<void*>(&nativeMoveBy) }, + { "nativeSetLonLat", "(JLcom/mapbox/mapboxgl/lib/LonLat;D)V", reinterpret_cast<void*>(&nativeSetLonLat) }, + { "nativeGetLonLat", "(J)Lcom/mapbox/mapboxgl/lib/LonLat;", reinterpret_cast<void*>(&nativeGetLonLat) }, + { "nativeStartPanning", "(J)V", reinterpret_cast<void*>(&nativeStartPanning) }, + { "nativeStopPanning", "(J)V", reinterpret_cast<void*>(&nativeStopPanning) }, + { "nativeResetPosition", "(J)V", reinterpret_cast<void*>(&nativeResetPosition) }, + { "nativeScaleBy", "(JDDDD)V", reinterpret_cast<void*>(&nativeScaleBy) }, + { "nativeSetScale", "(JDDDD)V", reinterpret_cast<void*>(&nativeSetScale) }, + { "nativeGetScale", "(J)D", reinterpret_cast<void*>(&nativeGetScale) }, + { "nativeSetZoom", "(JDD)V", reinterpret_cast<void*>(&nativeSetZoom) }, + { "nativeGetZoom", "(J)D", reinterpret_cast<void*>(&nativeGetZoom) }, + { "nativeSetLonLatZoom", "(JLcom/mapbox/mapboxgl/lib/LonLatZoom;D)V", reinterpret_cast<void*>(&nativeSetLonLatZoom) }, + { "nativeGetLonLatZoom", "(J)Lcom/mapbox/mapboxgl/lib/LonLatZoom;", reinterpret_cast<void*>(&nativeGetLonLatZoom) }, + { "nativeResetZoom", "(J)V", reinterpret_cast<void*>(&nativeResetZoom) }, + { "nativeStartPanning", "(J)V", reinterpret_cast<void*>(&nativeStartScaling) }, + { "nativeStopPanning", "(J)V", reinterpret_cast<void*>(&nativeStopScaling) }, + { "nativeGetMinZoom", "(J)D", reinterpret_cast<void*>(&nativeGetMinZoom) }, + { "nativeGetMaxZoom", "(J)D", reinterpret_cast<void*>(&nativeGetMaxZoom) }, + { "nativeRotateBy", "(JDDDDD)V", reinterpret_cast<void*>(&nativeRotateBy) }, + { "nativeSetAngle", "(JDD)V", reinterpret_cast<void*>(static_cast<void JNICALL(*)(JNIEnv*,jobject,jlong,jdouble,jdouble)>(&nativeSetAngle)) }, + { "nativeSetAngle", "(JDDD)V", reinterpret_cast<void*>(static_cast<void JNICALL(*)(JNIEnv*,jobject,jlong,jdouble,jdouble,jdouble)>(&nativeSetAngle)) }, + { "nativeGetAngle", "(J)D", reinterpret_cast<void*>(&nativeGetAngle) }, + { "nativeResetNorth", "(J)V", reinterpret_cast<void*>(&nativeResetNorth) }, + { "nativeStartRotating", "(J)V", reinterpret_cast<void*>(&nativeStartRotating) }, + { "nativeStopRotating", "(J)V", reinterpret_cast<void*>(&nativeStopRotating) }, + { "nativeCanRotate", "(J)Z", reinterpret_cast<void*>(&nativeCanRotate) }, + { "nativeSetDebug", "(JZ)V", reinterpret_cast<void*>(&nativeSetDebug) }, + { "nativeToggleDebug", "(J)V", reinterpret_cast<void*>(&nativeToggleDebug) }, + { "nativeGetDebug", "(J)Z", reinterpret_cast<void*>(&nativeGetDebug) } + }}; + + if (env->RegisterNatives(native_map_view_class, methods.data(), methods.size()) < 0) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_class = reinterpret_cast<jclass>(env->NewGlobalRef(lon_lat_class)); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_class = reinterpret_cast<jclass>(env->NewGlobalRef(lon_lat_zoom_class)); + if (lon_lat_zoom_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + return JNI_ERR; + } + + runtime_exception_class = reinterpret_cast<jclass>(env->NewGlobalRef(runtime_exception_class)); + if (runtime_exception_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + env->DeleteGlobalRef(lon_lat_zoom_class); + return JNI_ERR; + } + + tree_set_class = reinterpret_cast<jclass>(env->NewGlobalRef(tree_set_class)); + if (tree_set_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + env->DeleteGlobalRef(lon_lat_zoom_class); + env->DeleteGlobalRef(runtime_exception_class); + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} + +extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { + VERBOSE("JNI_OnUnload"); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return; + } + + env->DeleteGlobalRef(lon_lat_class); + lon_lat_class = nullptr; + lon_lat_constructor_id = nullptr; + lon_lat_lon_id = nullptr; + lon_lat_lat_id = nullptr; + + env->DeleteGlobalRef(lon_lat_zoom_class); + lon_lat_zoom_class = nullptr; + lon_lat_zoom_constructor_id = nullptr; + lon_lat_zoom_lon_id = nullptr; + lon_lat_zoom_lat_id = nullptr; + lon_lat_zoom_zoom_id = nullptr; + + on_map_changed_id = nullptr; + + env->DeleteGlobalRef(runtime_exception_class); + runtime_exception_class = nullptr; + + set_to_array_id = nullptr; + + env->DeleteGlobalRef(tree_set_class); + tree_set_class = nullptr; + tree_set_constructor_id = nullptr; + tree_set_add_id = nullptr; +} + +} // extern "C" diff --git a/android/NativeMapView.cpp b/android/NativeMapView.cpp new file mode 100644 index 0000000000..7ced2fa68d --- /dev/null +++ b/android/NativeMapView.cpp @@ -0,0 +1,528 @@ +//#include <cstdio> + +#include <memory> + +#include <GLES2/gl2.h> + +#include <mbgl/platform/platform.hpp> + +#include "log.h" + +#include "NativeMapView.hpp" + +namespace mbgl { +namespace android { + +void log_egl_string(EGLDisplay display, EGLint name, const char* label) { + const char* str = eglQueryString(display, name); + if (str == nullptr) { + ERROR("eglQueryString(%d) returned error %d", name, eglGetError()); + } else { + INFO("EGL %s: %s", label, str); + } +} + +void log_gl_string(GLenum name, const char* label) { + const GLubyte* str = glGetString(name); + if (str == nullptr) { + ERROR("glGetString(%d) returned error %d", name, glGetError()); + } else { + INFO("GL %s: %s", label, str); + } +} + +NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, + std::string default_style_json_) : + default_style_json(default_style_json_) { + VERBOSE("NativeMapView::NativeMapView"); + + ASSERT(env != nullptr); + ASSERT(obj_ != nullptr); + + if (env->GetJavaVM(&vm) < 0) { + env->ExceptionDescribe(); + return; + } + + obj = env->NewGlobalRef(obj_); + if (obj == nullptr) { + env->ExceptionDescribe(); + return; + } + + // TODO replace all printfs in map code with android logging + //freopen("/sdcard/stdout.txt", "w", stdout); // NOTE: can't use <cstdio> till NDK fix the stdout macro bug + //freopen("/sdcard/stderr.txt", "w", stderr); + + view = new MBGLView(this); + map = new mbgl::Map(*view); + + // FIXME need way to load this from Java + map->setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); + + // FIXME this asserts because it creates FileSource of different thread from run thread + //map->setStyleJSON(default_style_json); + map->setStyleURL("https://raw.githubusercontent.com/mapbox/mapbox-gl-styles/mb-pages/styles/bright-v5.json"); +} + +NativeMapView::~NativeMapView() { + VERBOSE("NativeMapView::~NativeMapView"); + terminateContext(); + + delete map; + map = nullptr; + + delete view; + view = nullptr; + + ASSERT(vm != nullptr); + ASSERT(obj != nullptr); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_OK) { + env->DeleteGlobalRef(obj); + } else { + ERROR("GetEnv() failed"); + } + obj = nullptr; + vm = nullptr; +} + +bool NativeMapView::initializeDisplay() { + VERBOSE("NativeMapView::initializeDisplay"); + + ASSERT(display == EGL_NO_DISPLAY); + ASSERT(config == nullptr); + ASSERT(format < 0); + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + ERROR("eglGetDisplay() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + + EGLint major, minor; + if (!eglInitialize(display, &major, &minor)) { + ERROR("eglInitialize() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + if ((major <= 1) && (minor < 3)) { + ERROR("EGL version is too low, need 1.3, got %d.%d", major, minor); + terminateDisplay(); + return false; + } + + log_egl_string(display, EGL_VENDOR, "Vendor"); + log_egl_string(display, EGL_VERSION, "Version"); + log_egl_string(display, EGL_CLIENT_APIS, "Client APIs"); + log_egl_string(display, EGL_EXTENSIONS, "Client Extensions"); + + // TODO should try 565, then 8888? + bool use565 = true; + + const EGLint config_attribs[] = { + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_BUFFER_SIZE, use565 ? 16 : 32, // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) + EGL_RED_SIZE, use565 ? 5 : 8, + EGL_GREEN_SIZE, use565 ? 6 : 8, + EGL_BLUE_SIZE, use565 ? 5 : 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + EGLint num_configs; + if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { + ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + if (num_configs < 1) { + ERROR("eglChooseConfig() returned no configs"); + terminateDisplay(); + return false; + } + + // TODO make_unique missing in Android NDK? +// const std::unique_ptr<EGLConfig[]> configs = std::make_unique(new EGLConfig[num_configs]); + const std::unique_ptr<EGLConfig[]> configs(new EGLConfig[num_configs]); + if (!eglChooseConfig(display, config_attribs, configs.get(), num_configs, + &num_configs)) { + ERROR("eglChooseConfig() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + + config = chooseConfig(configs.get(), num_configs, use565); + if (config == nullptr) { + ERROR("No config chosen"); + terminateDisplay(); + return false; + } + + if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { + ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + DEBUG("Chosen window format is %d", format); + + return true; +} + +void NativeMapView::terminateDisplay() { + VERBOSE("NativeMapView::terminateDisplay"); + + if (display != EGL_NO_DISPLAY) { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + if (!eglTerminate(display)) { + ERROR("eglTerminate() returned error %d", eglGetError()); + } + } + + display = EGL_NO_DISPLAY; + config = nullptr; + format = -1; +} + +bool NativeMapView::initializeContext() { + VERBOSE("NativeMapView::initializeContext"); + + ASSERT(display != EGL_NO_DISPLAY); + ASSERT(context == EGL_NO_CONTEXT); + ASSERT(config != nullptr); + + const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, + context_attribs); + if (context == EGL_NO_CONTEXT) { + ERROR("eglCreateContext() returned error %d", eglGetError()); + terminateContext(); + return false; + } + + INFO("Context initialized"); + + return true; +} + +void NativeMapView::terminateContext() { + VERBOSE("NativeMapView::terminateContext"); + + map->terminate(); + + // TODO: there is a bug when you double tap home to go app switcher, as map is black if you immediately switch to the map again + // TODO: this is in the onPause/onResume path + // TODO: the bug causes an GL_INVALID_VALUE with glDelteProgram (I think due to context being deleted first) + // TODO: we need to free resources before we terminate + // TODO: but cause terminate and stop is async they try to do stuff with no context and crash! + + if (display != EGL_NO_DISPLAY) { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + if (context != EGL_NO_CONTEXT) { + if (!eglDestroyContext(display, context)) { + ERROR("eglDestroyContext() returned error %d", eglGetError()); + } + } + } + + context = EGL_NO_CONTEXT; +} + +bool NativeMapView::createSurface(ANativeWindow* window_) { + VERBOSE("NativeMapView::createSurface"); + + 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 surface_attribs[] = { + EGL_NONE + }; + surface = eglCreateWindowSurface(display, config, window, surface_attribs); + if (surface == EGL_NO_SURFACE) { + ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); + destroySurface(); + return false; + } + + start(); + + return true; +} + +void NativeMapView::destroySurface() { + VERBOSE("NativeMapView::destroySurface"); + + if (surface != EGL_NO_SURFACE) { + if (!eglDestroySurface(display, surface)) { + ERROR("eglDestroySurface() returned error %d", eglGetError()); + } + } + + surface = EGL_NO_SURFACE; + + if (window != nullptr) { + ANativeWindow_release(window); + window = nullptr; + } +} + +EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], + EGLint num_configs, bool use565) const { + INFO("Found %d configs", num_configs); + + int chosen_config = -1; + for (int i = 0; i < num_configs; i++) { + INFO("Config %d:", i); + + EGLint bits, red, green, blue, alpha, alpha_mask, depth, stencil, + sample_buffers, samples; + + if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { + ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { + ERROR("eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { + ERROR("eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { + ERROR("eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { + ERROR("eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, + &alpha_mask)) { + ERROR("eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { + ERROR("eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, + &stencil)) { + ERROR("eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, + &sample_buffers)) { + ERROR("eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { + ERROR("eglGetConfigAttrib(EGL_SAMPLES) returned error %d", + eglGetError()); + return nullptr; + } + + INFO("Color: %d", bits); + INFO("Red: %d", red); + INFO("Green: %d", green); + INFO("Blue: %d", blue); + INFO("Alpha: %d", alpha); + INFO("Alpha mask: %d", alpha_mask); + INFO("Depth: %d", depth); + INFO("Stencil: %d", stencil); + INFO("Sample buffers: %d", sample_buffers); + INFO("Samples: %d", samples); + + bool config_ok = true; + config_ok &= bits == (use565 ? 16 : 32); + config_ok &= red == (use565 ? 5 : 8); + config_ok &= green == (use565 ? 6 : 8); + config_ok &= blue == (use565 ? 5 : 8); + //config_ok &= (alpha == 0) || (alpha == 8); // Can be either 0 for RGBX or 8 for RGBA but we don't care either way + config_ok &= alpha == 0; // TODO should prefer no alpha over alpha, and 565 over 8888? + //config_ok &= depth == 16; + //config_ok &= stencil == 8; + //config_ok &= sample_buffers == 0; + config_ok &= samples == 0; + + if (config_ok) { // Choose the last matching config, that way we get RGBX if possible (since it is sorted highest to lowest bits) + chosen_config = i; + } + } + + DEBUG("Chosen config is %d", chosen_config); + if (chosen_config >= 0) { + return configs[chosen_config]; + } else { + return nullptr; + } +} + +void NativeMapView::start() { + VERBOSE("NativeMapView::start"); + + if (display == EGL_NO_DISPLAY) { + initializeDisplay(); + } + + if (context == EGL_NO_CONTEXT) { + initializeContext(); + } + + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) + && (context != EGL_NO_CONTEXT)) { + + if (!eglMakeCurrent(display, surface, surface, context)) { + ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + + log_gl_string(GL_VENDOR, "Vendor"); + log_gl_string(GL_RENDERER, "Renderer"); + log_gl_string(GL_VERSION, "Version"); + log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); + log_gl_string(GL_EXTENSIONS, "Extensions"); + + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + map->start(); + } else { + DEBUG("Not starting because we are not ready"); + } +} + +void NativeMapView::stop() { + VERBOSE("NativeMapView::stop"); + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) + && (context != EGL_NO_CONTEXT)) { + map->stop(); + } +} + +void NativeMapView::notifyMapChange() { + DEBUG("NativeMapView::notify_map_change()"); + + ASSERT(vm != nullptr); + ASSERT(obj != nullptr); + + JNIEnv* env = nullptr; + // FIXME this returns failure + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return; + } + + env->CallVoidMethod(obj, on_map_changed_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } +} + +void MBGLView::make_active() { + VERBOSE("MBGLView::make_active"); + if ((nativeView->display != EGL_NO_DISPLAY) + && (nativeView->surface != EGL_NO_SURFACE) + && (nativeView->context != EGL_NO_CONTEXT)) { + if (!eglMakeCurrent(nativeView->display, nativeView->surface, + nativeView->surface, nativeView->context)) { + ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + + // TODO still can't fix the black screen problem :-( + EGLint width, height; + if (!(eglQuerySurface(nativeView->display, nativeView->surface, EGL_WIDTH, &width) + & eglQuerySurface(nativeView->display, nativeView->surface, EGL_HEIGHT, &height))) { + ERROR("eglQuerySurface() returned error %d", eglGetError()); + } + map->resize(width, height); + + } else { + DEBUG("Not activating as we are not ready"); + } +} + +void MBGLView::make_inactive() { + VERBOSE("MBGLView::make_inactive"); + if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } +} + +void MBGLView::swap() { + VERBOSE("MBGLView::swap"); + if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) + && (nativeView->surface != EGL_NO_SURFACE)) { + if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { + ERROR("eglSwapBuffers() returned error %d", eglGetError()); + } + map->swapped(); + } else { + DEBUG("Not swapping as we are not ready"); + } +} + +void MBGLView::notify() { + DEBUG("MBGLView::notify()"); + // TODO +} + +void MBGLView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { + DEBUG("MBGLView::notify_map_change()"); + nativeView->notifyMapChange(); + // TODO: use new variables + (void)change; + (void)delay; +} + +} // namespace android +} // namespace mbgl diff --git a/android/NativeMapView.hpp b/android/NativeMapView.hpp new file mode 100644 index 0000000000..046cd7c8fe --- /dev/null +++ b/android/NativeMapView.hpp @@ -0,0 +1,111 @@ +#ifndef MAP_VIEW_HPP +#define MAP_VIEW_HPP + +#include <string> + +#include <jni.h> + +#include <android/native_window.h> + +#include <EGL/egl.h> + +#include <mbgl/mbgl.hpp> + +namespace mbgl { +namespace android { + +extern jmethodID on_map_changed_id; + +extern jclass lon_lat_class; +extern jmethodID lon_lat_constructor_id; +extern jfieldID lon_lat_lon_id; +extern jfieldID lon_lat_lat_id; + +extern jclass lon_lat_zoom_class; +extern jmethodID lon_lat_zoom_constructor_id; +extern jfieldID lon_lat_zoom_lon_id; +extern jfieldID lon_lat_zoom_lat_id; +extern jfieldID lon_lat_zoom_zoom_id; + +extern jclass runtime_exception_class; + +extern jmethodID set_to_array_id; + +extern jclass tree_set_class; +extern jmethodID tree_set_constructor_id; +extern jmethodID tree_set_add_id; + +class MBGLView; + +class NativeMapView { + friend class MBGLView; + +public: + NativeMapView(JNIEnv* env, jobject obj, std::string default_style_json); + ~NativeMapView(); + + mbgl::Map* getMap() const { + return map; + } + + bool initializeDisplay(); + void terminateDisplay(); + + bool initializeContext(); + void terminateContext(); + + bool createSurface(ANativeWindow* window); + void destroySurface(); + + void start(); + void stop(); + + void notifyMapChange(); + +private: + EGLConfig chooseConfig(const EGLConfig configs[], EGLint num_configs, + bool use565) const; + +private: + JavaVM* vm = nullptr; + jobject obj = nullptr; + + ANativeWindow* window = nullptr; + + mbgl::Map* map = nullptr; + MBGLView* view = nullptr; + + EGLDisplay display = EGL_NO_DISPLAY; + EGLSurface surface = EGL_NO_SURFACE; + EGLContext context = EGL_NO_CONTEXT; + + EGLConfig config = nullptr; + EGLint format = -1; + + std::string default_style_json; +}; + +class MBGLView: public mbgl::View { +public: + MBGLView(NativeMapView* nativeView_) : + nativeView(nativeView_) { + } + virtual ~MBGLView() { + } + + void make_active() override; + void make_inactive() override; + + void swap() override; + + void notify() override; + void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0) override; + +private: + NativeMapView* nativeView = nullptr; +}; + +} // namespace android +} // namespace mbgl + +#endif // MAP_VIEW_HPP diff --git a/android/log.h b/android/log.h new file mode 100644 index 0000000000..461528a4cd --- /dev/null +++ b/android/log.h @@ -0,0 +1,18 @@ +#ifndef LOG_H +#define LOG_H + +#include <android/log.h> + +#define S(x) #x +#define S_(x) S(x) +#define S__LINE__ S_(__LINE__) + +#define VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) +#define DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) +#define INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) +#define WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) +#define ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) +#define ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) +#define ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) + +#endif // LOG_H diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp new file mode 100644 index 0000000000..203069ace7 --- /dev/null +++ b/android/mapboxgl-app.gyp @@ -0,0 +1,42 @@ +{ + 'includes': [ + '../gyp/common.gypi', + ], + 'targets': [ + { + 'target_name': 'androidapp', + 'product_name': 'mapbox-gl', + 'type': 'shared_library', + 'sources': [ + './NativeMapView.cpp', + './JNI.cpp', + ], + 'cflags_cc': [ + '-I<(boost_root)/include', + ], + 'variables': { + 'ldflags': [ + '-llog -landroid -lEGL -lGLESv2 ' + '<@(png_ldflags)', + '<@(sqlite3_ldflags)', + '<@(openssl_ldflags)', + '<@(curl_ldflags)', + '<@(zlib_ldflags)', + ], + }, + 'conditions': [ + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ '<@(ldflags)' ], + } + }, { + 'libraries': [ '<@(ldflags)' ], + }] + ], + 'dependencies': [ + '../mapboxgl.gyp:mbgl-standalone', + '../mapboxgl.gyp:mbgl-android', + ], + }, + ], +} |