summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2017-01-24 15:47:53 +0100
committerKonstantin Käfer <mail@kkaefer.com>2017-01-27 18:45:16 +0100
commit4d358260140c9d52103cc95631a8519b969bc408 (patch)
treedaf17b610189a824eab34e4055b9bc0ebf585375
parent14d5c336d72f3af54e3f72295e901591e219b5a9 (diff)
downloadqtlocation-mapboxgl-4d358260140c9d52103cc95631a8519b969bc408.tar.gz
[android] use native image encoding and decoding
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java12
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java40
-rw-r--r--platform/android/config.cmake13
-rw-r--r--platform/android/src/bitmap.cpp132
-rw-r--r--platform/android/src/bitmap.hpp52
-rw-r--r--platform/android/src/bitmap_factory.cpp25
-rw-r--r--platform/android/src/bitmap_factory.hpp25
-rw-r--r--platform/android/src/image.cpp22
-rwxr-xr-xplatform/android/src/jni.cpp6
-rwxr-xr-xplatform/android/src/native_map_view.cpp10
10 files changed, 278 insertions, 59 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index 18af6e8f2e..24b43b2e82 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1604,18 +1604,8 @@ public final class MapboxMap {
* @param bitmap A pre-allocated bitmap.
*/
@UiThread
- public void snapshot(@NonNull SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
- nativeMapView.addSnapshotCallback(callback, bitmap);
- }
-
- /**
- * Takes a snapshot of the map.
- *
- * @param callback Callback method invoked when the snapshot is taken.
- */
- @UiThread
public void snapshot(@NonNull SnapshotReadyCallback callback) {
- snapshot(callback, null);
+ nativeMapView.addSnapshotCallback(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 654923ea96..c7defa7bc7 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
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.maps;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
@@ -54,7 +53,7 @@ final class NativeMapView {
private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners;
// Listener invoked to return a bitmap of the map
- private SnapshotRequest snapshotRequest;
+ private MapboxMap.SnapshotReadyCallback snapshotReadyCallback;
//
// Static methods
@@ -935,18 +934,9 @@ final class NativeMapView {
mapView.onFpsChanged(fps);
}
- protected void onSnapshotReady(byte[] bytes) {
- if (snapshotRequest != null && bytes != null) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inBitmap = snapshotRequest.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 = snapshotRequest.getCallback();
- if (callback != null) {
- callback.onSnapshotReady(bitmap);
- }
+ protected void onSnapshotReady(Bitmap bitmap) {
+ if (snapshotReadyCallback != null && bitmap != null) {
+ snapshotReadyCallback.onSnapshotReady(bitmap);
}
}
@@ -1187,27 +1177,9 @@ final class NativeMapView {
// Snapshot
//
- void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback, @Nullable Bitmap bitmap) {
- snapshotRequest = new SnapshotRequest(bitmap, callback);
+ void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) {
+ snapshotReadyCallback = callback;
scheduleTakeSnapshot();
render();
}
-
- private static class SnapshotRequest {
- private Bitmap bitmap;
- private MapboxMap.SnapshotReadyCallback callback;
-
- 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/config.cmake b/platform/android/config.cmake
index 326ddb612a..7d554dc786 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -19,8 +19,6 @@ if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR
endif()
mason_use(jni.hpp VERSION 2.0.0 HEADER_ONLY)
-mason_use(libjpeg-turbo VERSION 1.5.0)
-mason_use(libpng VERSION 1.6.25)
mason_use(libzip VERSION 1.1.3)
mason_use(nunicode VERSION 1.7.1)
mason_use(sqlite VERSION 3.14.2)
@@ -68,10 +66,12 @@ macro(mbgl_platform_core)
PRIVATE platform/default/utf.cpp
# Image handling
- PRIVATE platform/default/image.cpp
PRIVATE platform/default/png_writer.cpp
- PRIVATE platform/default/png_reader.cpp
- PRIVATE platform/default/jpeg_reader.cpp
+ PRIVATE platform/android/src/bitmap.cpp
+ PRIVATE platform/android/src/bitmap.hpp
+ PRIVATE platform/android/src/bitmap_factory.cpp
+ PRIVATE platform/android/src/bitmap_factory.hpp
+ PRIVATE platform/android/src/image.cpp
# Thread pool
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
@@ -147,8 +147,6 @@ macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
- target_add_mason_package(mbgl-core PUBLIC libpng)
- target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo)
target_add_mason_package(mbgl-core PUBLIC libzip)
target_add_mason_package(mbgl-core PUBLIC geojson)
target_add_mason_package(mbgl-core PUBLIC jni.hpp)
@@ -165,6 +163,7 @@ macro(mbgl_platform_core)
target_link_libraries(mbgl-core
PUBLIC -llog
PUBLIC -landroid
+ PUBLIC -ljnigraphics
PUBLIC -lEGL
PUBLIC -lGLESv2
PUBLIC -lstdc++
diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp
new file mode 100644
index 0000000000..50088116f4
--- /dev/null
+++ b/platform/android/src/bitmap.cpp
@@ -0,0 +1,132 @@
+#include "bitmap.hpp"
+
+#include <android/bitmap.h>
+
+namespace mbgl {
+namespace android {
+
+class PixelGuard {
+public:
+ PixelGuard(jni::JNIEnv& env_, jni::Object<Bitmap> bitmap_) : env(env_), bitmap(bitmap_) {
+ const int result = AndroidBitmap_lockPixels(&env, jni::Unwrap(*bitmap),
+ reinterpret_cast<void**>(&address));
+ if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
+ throw std::runtime_error("bitmap decoding: could not lock pixels");
+ }
+ }
+ ~PixelGuard() {
+ const int result = AndroidBitmap_unlockPixels(&env, jni::Unwrap(*bitmap));
+ if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
+ throw std::runtime_error("bitmap decoding: could not unlock pixels");
+ }
+ }
+
+ auto* get() {
+ return address;
+ }
+
+ const auto* get() const {
+ return address;
+ }
+
+private:
+ jni::JNIEnv& env;
+ jni::Object<Bitmap> bitmap;
+ uint8_t* address;
+};
+
+void Bitmap::Config::registerNative(jni::JNIEnv& env) {
+ _class = *jni::Class<Config>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Bitmap::Config> Bitmap::Config::_class;
+
+jni::Object<Bitmap::Config> Bitmap::Config::Create(jni::JNIEnv& env, Value value) {
+ switch (value) {
+ case ALPHA_8:
+ return _class.Get(env,
+ jni::StaticField<Config, jni::Object<Config>>(env, _class, "ALPHA_8"));
+ case ARGB_4444:
+ return _class.Get(env,
+ jni::StaticField<Config, jni::Object<Config>>(env, _class, "ARGB_4444"));
+ case ARGB_8888:
+ return _class.Get(env,
+ jni::StaticField<Config, jni::Object<Config>>(env, _class, "ARGB_8888"));
+ case RGB_565:
+ return _class.Get(env,
+ jni::StaticField<Config, jni::Object<Config>>(env, _class, "RGB_565"));
+ default:
+ throw std::runtime_error("invalid enum value for Bitmap.Config");
+ }
+}
+
+void Bitmap::registerNative(jni::JNIEnv& env) {
+ _class = *jni::Class<Bitmap>::Find(env).NewGlobalRef(env).release();
+ Config::registerNative(env);
+}
+
+jni::Class<Bitmap> Bitmap::_class;
+
+jni::Object<Bitmap> Bitmap::CreateBitmap(jni::JNIEnv& env,
+ jni::jint width,
+ jni::jint height,
+ jni::Object<Config> config) {
+ using Signature = jni::Object<Bitmap>(jni::jint, jni::jint, jni::Object<Config>);
+ auto method = _class.GetStaticMethod<Signature>(env, "createBitmap");
+ return _class.Call(env, method, width, height, config);
+}
+
+jni::Object<Bitmap> Bitmap::CreateBitmap(jni::JNIEnv& env, const PremultipliedImage& image) {
+ auto bitmap = CreateBitmap(env, image.size.width, image.size.height, Config::ARGB_8888);
+
+ AndroidBitmapInfo info;
+ const int result = AndroidBitmap_getInfo(&env, jni::Unwrap(*bitmap), &info);
+ if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
+ // TODO: more specific information
+ throw std::runtime_error("bitmap creation: couldn't get bitmap info");
+ }
+
+ assert(info.width == image.size.width);
+ assert(info.height == image.size.height);
+ assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+ PixelGuard guard(env, bitmap);
+
+ // Copy the PremultipliedImage into the Android Bitmap
+ for (uint32_t y = 0; y < image.size.height; y++) {
+ auto begin = image.data.get() + y * image.stride();
+ std::copy(begin, begin + image.stride(), guard.get() + y * info.stride);
+ }
+
+ return bitmap;
+}
+
+PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) {
+ AndroidBitmapInfo info;
+ const int result = AndroidBitmap_getInfo(&env, jni::Unwrap(*bitmap), &info);
+ if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
+ // TODO: more specific information
+ throw std::runtime_error("bitmap decoding: couldn't get bitmap info");
+ }
+
+ if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
+ // TODO: convert
+ throw std::runtime_error("bitmap decoding: bitmap format invalid");
+ }
+
+ const PixelGuard guard(env, bitmap);
+
+ // Copy the Android Bitmap into the PremultipliedImage.
+ auto pixels =
+ std::make_unique<uint8_t[]>(info.width * info.height * PremultipliedImage::channels);
+ for (uint32_t y = 0; y < info.height; y++) {
+ auto begin = guard.get() + y * info.stride;
+ std::copy(begin, begin + info.width * PremultipliedImage::channels,
+ pixels.get() + y * info.width * PremultipliedImage::channels);
+ }
+
+ return { Size{ info.width, info.height }, std::move(pixels) };
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/bitmap.hpp b/platform/android/src/bitmap.hpp
new file mode 100644
index 0000000000..f64f42ae87
--- /dev/null
+++ b/platform/android/src/bitmap.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class Bitmap {
+public:
+ class Config {
+ public:
+ static constexpr auto Name() {
+ return "android/graphics/Bitmap$Config";
+ };
+ static void registerNative(jni::JNIEnv&);
+
+ enum Value {
+ ALPHA_8,
+ ARGB_4444,
+ ARGB_8888,
+ RGB_565,
+ };
+
+ static jni::Object<Config> Create(jni::JNIEnv&, Value);
+
+ private:
+ static jni::Class<Config> _class;
+ };
+
+ static constexpr auto Name() {
+ return "android/graphics/Bitmap";
+ };
+ static void registerNative(jni::JNIEnv&);
+
+ static jni::Object<Bitmap>
+ CreateBitmap(jni::JNIEnv&, jni::jint width, jni::jint height, jni::Object<Config>);
+ static jni::Object<Bitmap>
+ CreateBitmap(jni::JNIEnv& env, jni::jint width, jni::jint height, Config::Value config) {
+ return CreateBitmap(env, width, height, Config::Create(env, config));
+ }
+
+ static PremultipliedImage GetImage(jni::JNIEnv&, jni::Object<Bitmap>);
+ static jni::Object<Bitmap> CreateBitmap(jni::JNIEnv&, const PremultipliedImage&);
+
+private:
+ static jni::Class<Bitmap> _class;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/bitmap_factory.cpp b/platform/android/src/bitmap_factory.cpp
new file mode 100644
index 0000000000..7d9b068b20
--- /dev/null
+++ b/platform/android/src/bitmap_factory.cpp
@@ -0,0 +1,25 @@
+#include "bitmap_factory.hpp"
+
+namespace mbgl {
+namespace android {
+
+void BitmapFactory::registerNative(jni::JNIEnv& env) {
+ _class = *jni::Class<BitmapFactory>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<BitmapFactory> BitmapFactory::_class;
+
+jni::Object<Bitmap> BitmapFactory::DecodeByteArray(jni::JNIEnv& env,
+ jni::Array<jni::jbyte> data,
+ jni::jint offset,
+ jni::jint length) {
+
+ // Images are loaded with ARGB_8888 config, and premultiplied by default, which is exactly
+ // what we want, so we're not providing a BitmapFactory.Options object.
+ using Signature = jni::Object<Bitmap>(jni::Array<jni::jbyte>, jni::jint, jni::jint);
+ auto method = _class.GetStaticMethod<Signature>(env, "decodeByteArray");
+ return _class.Call(env, method, data, offset, length);
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/bitmap_factory.hpp b/platform/android/src/bitmap_factory.hpp
new file mode 100644
index 0000000000..b0e7198260
--- /dev/null
+++ b/platform/android/src/bitmap_factory.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+#include "bitmap.hpp"
+
+namespace mbgl {
+namespace android {
+
+class BitmapFactory {
+public:
+ static constexpr auto Name() {
+ return "android/graphics/BitmapFactory";
+ };
+ static void registerNative(jni::JNIEnv&);
+
+ static jni::Object<Bitmap>
+ DecodeByteArray(jni::JNIEnv&, jni::Array<jni::jbyte> data, jni::jint offset, jni::jint length);
+
+private:
+ static jni::Class<BitmapFactory> _class;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/image.cpp b/platform/android/src/image.cpp
new file mode 100644
index 0000000000..2a33944b18
--- /dev/null
+++ b/platform/android/src/image.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <string>
+
+#include "attach_env.hpp"
+#include "bitmap_factory.hpp"
+
+namespace mbgl {
+
+PremultipliedImage decodeImage(const std::string& string) {
+ auto env{ android::AttachEnv() };
+
+ auto array = jni::Array<jni::jbyte>::New(*env, string.size());
+ jni::SetArrayRegion(*env, *array, 0, string.size(),
+ reinterpret_cast<const signed char*>(string.data()));
+
+ auto bitmap = android::BitmapFactory::DecodeByteArray(*env, array, 0, string.size());
+ return android::Bitmap::GetImage(*env, bitmap);
+}
+
+} // namespace mbgl
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 009de89db2..f273210c77 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -11,6 +11,8 @@
#include "jni.hpp"
#include "java_types.hpp"
#include "native_map_view.hpp"
+#include "bitmap.hpp"
+#include "bitmap_factory.hpp"
#include "connectivity_listener.hpp"
#include "style/layers/layers.hpp"
#include "style/sources/sources.hpp"
@@ -1766,6 +1768,8 @@ void registerNatives(JavaVM *vm) {
mbgl::android::RegisterNativeHTTPRequest(env);
java::registerNatives(env);
+ Bitmap::registerNative(env);
+ BitmapFactory::registerNative(env);
registerNativeLayers(env);
registerNativeSources(env);
ConnectivityListener::registerNative(env);
@@ -1841,7 +1845,7 @@ void registerNatives(JavaVM *vm) {
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");
+ onSnapshotReadyId = &jni::GetMethodID(env, nativeMapViewClass, "onSnapshotReady","(Landroid/graphics/Bitmap;)V");
#define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod<decltype(name), name>( #name, sig )
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 9d467f6ede..663b38963d 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -18,6 +18,8 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/image.hpp>
+#include "bitmap.hpp"
+
namespace mbgl {
namespace android {
@@ -172,14 +174,10 @@ void NativeMapView::render() {
// take snapshot
auto image = getContext().readFramebuffer<mbgl::PremultipliedImage>(getFramebufferSize());
-
- // 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());
+ auto bitmap = Bitmap::CreateBitmap(*env, std::move(image));
// invoke Mapview#OnSnapshotReady
- env->CallVoidMethod(obj, onSnapshotReadyId, arr);
+ env->CallVoidMethod(obj, onSnapshotReadyId, jni::Unwrap(*bitmap));
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}