summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobrun Van Nuland <tobrun.van.nuland@gmail.com>2016-07-27 18:39:32 +0200
committerTobrun <tobrun.van.nuland@gmail.com>2016-08-03 14:55:45 -0400
commit6016b2981e73be81f6a74a789ffabb51b7e91700 (patch)
tree4a3a4f50978dbfd2ff9ca304ce7c8638819a973d
parent9b871a9a0fcbbeb40cec07fc976cbdbaebf00856 (diff)
downloadqtlocation-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
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java62
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java10
-rwxr-xr-xplatform/android/src/jni.cpp12
-rw-r--r--platform/android/src/jni.hpp1
-rwxr-xr-xplatform/android/src/native_map_view.cpp33
-rwxr-xr-xplatform/android/src/native_map_view.hpp3
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;