This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[android] use native image encoding and decoding
- Loading branch information
Showing
10 changed files
with
278 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.
4d35826
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kkaefer - could Bitmap::GetImage be used for NativeMapView.addImage too?
4d35826
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaegs say more? It already uses the Android image decoding routines.
4d35826
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bitmaps are stored in native memory so currently NativeMapView.addImage is copying the bitmap into Java and then sending it back to native whereas Bitmap::GetImage does a direct native-to-native copy. I tested using Bitmap::GetImage and it saves around .3ms for each of the some small icons I tried on a Pixel phone. I'll make a PR.
4d35826
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, understood. Thanks!