[android] use native image encoding and decoding
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
|
||||
132
platform/android/src/bitmap.cpp
Normal file
132
platform/android/src/bitmap.cpp
Normal file
@@ -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
|
||||
52
platform/android/src/bitmap.hpp
Normal file
52
platform/android/src/bitmap.hpp
Normal file
@@ -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
|
||||
25
platform/android/src/bitmap_factory.cpp
Normal file
25
platform/android/src/bitmap_factory.cpp
Normal file
@@ -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
|
||||
25
platform/android/src/bitmap_factory.hpp
Normal file
25
platform/android/src/bitmap_factory.hpp
Normal file
@@ -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
|
||||
22
platform/android/src/image.cpp
Normal file
22
platform/android/src/image.cpp
Normal file
@@ -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
|
||||
@@ -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 )
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user