diff options
author | Tobrun Van Nuland <tobrun.van.nuland@gmail.com> | 2016-07-27 18:39:32 +0200 |
---|---|---|
committer | Tobrun <tobrun.van.nuland@gmail.com> | 2016-08-03 14:55:45 -0400 |
commit | 6016b2981e73be81f6a74a789ffabb51b7e91700 (patch) | |
tree | 4a3a4f50978dbfd2ff9ca304ce7c8638819a973d | |
parent | 9b871a9a0fcbbeb40cec07fc976cbdbaebf00856 (diff) | |
download | qtlocation-mapboxgl-6016b2981e73be81f6a74a789ffabb51b7e91700.tar.gz |
[android] SurfaceView compatible snapshot api
[android] #5587 - snapshot API on surface view, create Bitmap with a ByteBuffer
[android] #5587 - write bitmap to external storage
PreMultipliedImage approach
callback implemented
clean up code
introduce bitmap reuse
cleanup
renaming surfaceContent to bitmap
6 files changed, 105 insertions, 16 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 95ede2f17d..6b0c207a1a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -13,6 +13,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.PointF; import android.graphics.RectF; @@ -157,6 +158,7 @@ public class MapView extends FrameLayout { private String mInitalStyle; private List<OnMapReadyCallback> mOnMapReadyCallbackList; + private SnapshotRequest mSnapshotRequest; @UiThread public MapView(@NonNull Context context) { @@ -196,7 +198,7 @@ public class MapView extends FrameLayout { // Reference the TextureView SurfaceView surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView); - + // Check if we are in Android Studio UI editor to avoid error in layout preview if (isInEditMode()) { return; @@ -1838,7 +1840,7 @@ public class MapView extends FrameLayout { return false; } - // requestDisallowInterceptTouchEvent(true); + requestDisallowInterceptTouchEvent(true); // reset tracking modes if gesture occurs resetTrackingModesIfRequired(); @@ -2652,21 +2654,51 @@ public class MapView extends FrameLayout { return mNativeMapView; } + // + // Snapshot API + // + @UiThread void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) { -// TextureView textureView = (TextureView) findViewById(R.id.textureView); -// final boolean canUseBitmap = bitmap != null && textureView.getWidth() == bitmap.getWidth() && textureView.getHeight() == bitmap.getHeight(); -// -// setDrawingCacheEnabled(true); -// Bitmap content = Bitmap.createBitmap(getDrawingCache()); -// setDrawingCacheEnabled(false); -// -// Bitmap output = Bitmap.createBitmap(content.getWidth(), content.getHeight(), Bitmap.Config.ARGB_8888); -// Canvas canvas = new Canvas(output); -// canvas.drawBitmap(canUseBitmap ? textureView.getBitmap(bitmap) : textureView.getBitmap(), 0, 0, null); -// canvas.drawBitmap(content, new Matrix(), null); -// callback.onSnapshotReady(output); - throw new RuntimeException("TextureView code needs to be migrated to SurfaceView"); + mSnapshotRequest = new SnapshotRequest(bitmap, callback); + mNativeMapView.scheduleTakeSnapshot(); + mNativeMapView.render(); + } + + // Called when the snapshot method was executed + // Called via JNI from NativeMapView + // Forward to any listeners + protected void onSnapshotReady(byte[] bytes) { + if (mSnapshotRequest != null && bytes != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inBitmap = mSnapshotRequest.getBitmap(); // the old Bitmap to be reused + options.inMutable = true; + options.inSampleSize = 1; + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + + MapboxMap.SnapshotReadyCallback callback = mSnapshotRequest.getCallback(); + if (callback != null) { + callback.onSnapshotReady(bitmap); + } + } + } + + private class SnapshotRequest { + private Bitmap bitmap; + private MapboxMap.SnapshotReadyCallback callback; + + public SnapshotRequest(Bitmap bitmap, MapboxMap.SnapshotReadyCallback callback) { + this.bitmap = bitmap; + this.callback = callback; + } + + public Bitmap getBitmap() { + return bitmap; + } + + public MapboxMap.SnapshotReadyCallback getCallback() { + return callback; + } } // diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 0adc551d8d..929f515d8d 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -501,6 +501,10 @@ final class NativeMapView { nativeRemoveSource(mNativeMapViewPtr, sourceId); } + public void scheduleTakeSnapshot() { + nativeScheduleTakeSnapshot(mNativeMapViewPtr); + } + // // Callbacks // @@ -517,6 +521,10 @@ final class NativeMapView { mMapView.onFpsChanged(fps); } + protected void onSnapshotReady(byte[] bytes) { + mMapView.onSnapshotReady(bytes); + } + // // JNI methods // @@ -686,4 +694,6 @@ final class NativeMapView { private native long nativeUpdatePolygon(long nativeMapViewPtr, long polygonId, Polygon polygon); private native long nativeUpdatePolyline(long nativeMapviewPtr, long polylineId, Polyline polyline); + + private native void nativeScheduleTakeSnapshot(long nativeMapViewPtr); } diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 5fc29f4c29..5907a0ff9d 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -45,6 +45,7 @@ std::string androidRelease; jni::jmethodID* onInvalidateId = nullptr; jni::jmethodID* onMapChangedId = nullptr; jni::jmethodID* onFpsChangedId = nullptr; +jni::jmethodID* onSnapshotReadyId = nullptr; jni::jclass* latLngClass = nullptr; jni::jmethodID* latLngConstructorId = nullptr; @@ -1172,6 +1173,13 @@ void nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, } } +void nativeScheduleTakeSnapshot(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { + mbgl::Log::Error(mbgl::Event::JNI, "nativeRenderToOffscreen"); + assert(nativeMapViewPtr != 0); + NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); + nativeMapView->scheduleTakeSnapshot(); +} + // Offline calls begin jlong createDefaultFileSource(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* assetRoot_, jlong maximumCacheSize) { @@ -1706,6 +1714,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { onInvalidateId = &jni::GetMethodID(env, nativeMapViewClass, "onInvalidate", "()V"); onMapChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onMapChanged", "(I)V"); onFpsChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onFpsChanged", "(D)V"); + onSnapshotReadyId = &jni::GetMethodID(env, nativeMapViewClass, "onSnapshotReady","([B)V"); #define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod<decltype(name), name>( #name, sig ) @@ -1786,7 +1795,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { MAKE_NATIVE_METHOD(nativeRemoveLayer, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeAddSource, "(JLjava/lang/String;Lcom/mapbox/mapboxsdk/style/sources/Source;)V"), MAKE_NATIVE_METHOD(nativeRemoveSource, "(JLjava/lang/String;)V"), - MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V") + MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"), + MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V") ); // Offline begin diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp index 57f84f0ccc..0810ee656d 100644 --- a/platform/android/src/jni.hpp +++ b/platform/android/src/jni.hpp @@ -19,6 +19,7 @@ extern std::string androidRelease; extern jmethodID onInvalidateId; extern jmethodID onMapChangedId; extern jmethodID onFpsChangedId; +extern jmethodID onSnapshotReadyId; extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName); extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach); diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 0b849976eb..578e5d0033 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -194,6 +194,35 @@ void NativeMapView::render() { map->render(); + if(snapshot){ + snapshot = false; + + // take snapshot + const unsigned int w = fbWidth; + const unsigned int h = fbHeight; + mbgl::PremultipliedImage image { w, h }; + MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); + const size_t stride = image.stride(); + auto tmp = std::make_unique<uint8_t[]>(stride); + uint8_t *rgba = image.data.get(); + for (int i = 0, j = h - 1; i < j; i++, j--) { + std::memcpy(tmp.get(), rgba + i * stride, stride); + std::memcpy(rgba + i * stride, rgba + j * stride, stride); + std::memcpy(rgba + j * stride, tmp.get(), stride); + } + + // encode and convert to jbytes + std::string string = encodePNG(image); + jbyteArray arr = env->NewByteArray(string.length()); + env->SetByteArrayRegion(arr,0,string.length(),(jbyte*)string.c_str()); + + // invoke Mapview#OnSnapshotReady + env->CallVoidMethod(obj, onSnapshotReadyId, arr); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + } + } + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(display, surface)) { mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", @@ -436,6 +465,10 @@ void NativeMapView::destroySurface() { } } +void NativeMapView::scheduleTakeSnapshot() { + snapshot = true; +} + // Speed /* typedef enum { diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 40cb012b0b..9b5af76dfe 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -49,6 +49,8 @@ public: mbgl::EdgeInsets getInsets() { return insets;} void setInsets(mbgl::EdgeInsets insets_); + void scheduleTakeSnapshot(); + private: EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); @@ -79,6 +81,7 @@ private: bool firstTime = false; bool fpsEnabled = false; bool sizeChanged = false; + bool snapshot = false; double fps = 0.0; int width = 0; |