diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2017-01-24 15:47:53 +0100 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2017-01-27 18:45:16 +0100 |
commit | 4d358260140c9d52103cc95631a8519b969bc408 (patch) | |
tree | daf17b610189a824eab34e4055b9bc0ebf585375 | |
parent | 14d5c336d72f3af54e3f72295e901591e219b5a9 (diff) | |
download | qtlocation-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.java | 12 | ||||
-rwxr-xr-x | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 40 | ||||
-rw-r--r-- | platform/android/config.cmake | 13 | ||||
-rw-r--r-- | platform/android/src/bitmap.cpp | 132 | ||||
-rw-r--r-- | platform/android/src/bitmap.hpp | 52 | ||||
-rw-r--r-- | platform/android/src/bitmap_factory.cpp | 25 | ||||
-rw-r--r-- | platform/android/src/bitmap_factory.hpp | 25 | ||||
-rw-r--r-- | platform/android/src/image.cpp | 22 | ||||
-rwxr-xr-x | platform/android/src/jni.cpp | 6 | ||||
-rwxr-xr-x | platform/android/src/native_map_view.cpp | 10 |
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(); } |