summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Loer <chris.loer@gmail.com>2017-11-30 12:38:06 -0800
committerChris Loer <chris.loer@mapbox.com>2017-12-11 10:43:00 -0800
commit00ed34f7d700ae255eae1af1ca46b939ffee219d (patch)
treed931d219ebb4a309a8dfad70c8ee159e971ae471
parent3713102005b8243ade727f8084a6236954c1a1d6 (diff)
downloadqtlocation-mapboxgl-00ed34f7d700ae255eae1af1ca46b939ffee219d.tar.gz
[android] Android implementation of local CJK glyph rendering
- Draws bold version of glyph if font stack contains string "bold" - Not hooked up to global configuration yet
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java46
-rw-r--r--platform/android/config.cmake3
-rwxr-xr-xplatform/android/src/jni.cpp4
-rw-r--r--platform/android/src/text/local_glyph_rasterizer.cpp126
-rw-r--r--platform/android/src/text/local_glyph_rasterizer_jni.hpp31
5 files changed, 209 insertions, 1 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
new file mode 100644
index 0000000000..920a1270ac
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
@@ -0,0 +1,46 @@
+package com.mapbox.mapboxsdk.text;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Bitmap;
+import android.graphics.Typeface;
+import android.support.annotation.WorkerThread;
+
+/**
+ * LocalGlyphRasterizer is the Android-specific platform implementation used
+ * by the portable local_glyph_rasterizer.hpp
+ */
+public class LocalGlyphRasterizer {
+
+ /***
+ * Uses Android-native drawing code to rasterize a single glyph
+ * to a square @{link Bitmap} which can be returned to portable
+ * code for transformation into a Signed Distance Field glyph.
+ *
+ * @param fontFamily Font family string to pass to Typeface.create
+ * @param bold If true, use Typeface.BOLD option
+ * @param glyphID 16-bit Unicode BMP codepoint to draw
+ *
+ * @return Return a @{link Bitmap} to be displayed in the requested tile.
+ */
+ @WorkerThread
+ protected static Bitmap drawGlyphBitmap(String fontFamily, boolean bold, char glyphID) {
+ /*
+ 35x35px dimensions are hardwired to match local_glyph_rasterizer.cpp
+ These dimensions are large enough to draw a 24 point character in the middle
+ of the bitmap (y: 20) with some buffer around the edge
+ */
+ Bitmap bitmap = Bitmap.createBitmap(35, 35, Bitmap.Config.ARGB_8888);
+
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setTextSize(24);
+ paint.setTypeface(Typeface.create(fontFamily, bold ? Typeface.BOLD : Typeface.NORMAL));
+
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+ canvas.drawText(String.valueOf(glyphID), 0, 20, paint);
+
+ return bitmap;
+ }
+}
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index a915f1d93d..f5de7a6052 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -37,11 +37,12 @@ macro(mbgl_platform_core)
PRIVATE platform/android/src/timer.cpp
# Misc
+ PRIVATE platform/android/src/text/local_glyph_rasterizer_jni.hpp
+ PRIVATE platform/android/src/text/local_glyph_rasterizer.cpp
PRIVATE platform/android/src/logging_android.cpp
PRIVATE platform/android/src/thread.cpp
PRIVATE platform/default/string_stdlib.cpp
PRIVATE platform/default/bidi.cpp
- PRIVATE platform/default/local_glyph_rasterizer.cpp
PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 63566e1772..88ad0edb9e 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -51,6 +51,7 @@
#include "style/light.hpp"
#include "snapshotter/map_snapshotter.hpp"
#include "snapshotter/map_snapshot.hpp"
+#include "text/local_glyph_rasterizer_jni.hpp"
namespace mbgl {
namespace android {
@@ -188,6 +189,9 @@ void registerNatives(JavaVM *vm) {
// Snapshotter
MapSnapshotter::registerNative(env);
MapSnapshot::registerNative(env);
+
+ // text
+ LocalGlyphRasterizer::registerNative(env);
}
} // namespace android
diff --git a/platform/android/src/text/local_glyph_rasterizer.cpp b/platform/android/src/text/local_glyph_rasterizer.cpp
new file mode 100644
index 0000000000..ce1b0fc8fd
--- /dev/null
+++ b/platform/android/src/text/local_glyph_rasterizer.cpp
@@ -0,0 +1,126 @@
+#include <mbgl/text/local_glyph_rasterizer.hpp>
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/platform.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../attach_env.hpp"
+#include "../bitmap.hpp"
+
+#include "local_glyph_rasterizer_jni.hpp"
+
+/*
+ Android implementation of LocalGlyphRasterizer:
+ Draws CJK glyphs using locally available fonts.
+
+ Follows pattern of GL JS implementation in that:
+ - Only CJK glyphs are drawn locally (because we can guess their metrics effectively)
+ * Render size/metrics determined experimentally using Noto Sans
+ - Configuration is done at map creation time by setting a "font family"
+ * JS uses a CSS font-family, this uses android.graphics.Typeface
+ https://developer.android.com/reference/android/graphics/Typeface.html
+ - We use heuristics to extract a font-weight based on the incoming font stack
+ * JS tries to extract multiple weights, this implementation only looks for
+ "bold"
+
+ mbgl::LocalGlyphRasterizer is the portable interface
+ mbgl::LocalGlyphRasterizer::Impl stores platform-specific configuration data
+ mbgl::android::LocalGlyphRasterizer is the JNI wrapper
+ com.mapbox.mapboxsdk.text.LocalGlyphRasterizer is the Java implementation that
+ actually does the drawing
+ */
+
+namespace mbgl {
+namespace android {
+
+PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& fontFamily, const bool bold, const GlyphID glyphID) {
+ UniqueEnv env { AttachEnv() };
+
+ using Signature = jni::Object<Bitmap>(jni::String, jni::jboolean, jni::jchar);
+ auto method = javaClass.GetStaticMethod<Signature>(*env, "drawGlyphBitmap");
+
+ jni::String jniFontFamily = jni::Make<jni::String>(*env, fontFamily);
+
+ auto javaBitmap = javaClass.Call(*env,
+ method,
+ jniFontFamily,
+ static_cast<jni::jboolean>(bold),
+ static_cast<jni::jchar>(glyphID));
+
+ PremultipliedImage result = Bitmap::GetImage(*env, javaBitmap);
+ jni::DeleteLocalRef(*env, javaBitmap);
+ return result;
+}
+
+void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) {
+ javaClass = *jni::Class<LocalGlyphRasterizer>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<LocalGlyphRasterizer> LocalGlyphRasterizer::javaClass;
+
+} // namespace android
+
+class LocalGlyphRasterizer::Impl {
+public:
+ Impl(const optional<std::string> fontFamily_)
+ : fontFamily(fontFamily_)
+ {}
+
+ bool isConfigured() const {
+ return bool(fontFamily);
+ }
+
+ PremultipliedImage drawGlyphBitmap(const FontStack& fontStack, GlyphID glyphID) {
+ bool bold = false;
+ for (auto font : fontStack) {
+ std::string lowercaseFont = platform::lowercase(font);
+ if (lowercaseFont.find("bold") != std::string::npos) {
+ bold = true;
+ }
+ }
+ return android::LocalGlyphRasterizer::drawGlyphBitmap(*fontFamily, bold, glyphID);
+ }
+
+private:
+ optional<std::string> fontFamily;
+};
+
+LocalGlyphRasterizer::LocalGlyphRasterizer(const optional<std::string> fontFamily)
+ : impl(std::make_unique<Impl>(fontFamily))
+{}
+
+LocalGlyphRasterizer::~LocalGlyphRasterizer()
+{}
+
+bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isConfigured();
+}
+
+Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID glyphID) {
+ Glyph fixedMetrics;
+ if (!impl->isConfigured()) {
+ return fixedMetrics;
+ }
+
+ fixedMetrics.id = glyphID;
+
+ Size size(35, 35);
+
+ fixedMetrics.metrics.width = size.width;
+ fixedMetrics.metrics.height = size.height;
+ fixedMetrics.metrics.left = 3;
+ fixedMetrics.metrics.top = -10;
+ fixedMetrics.metrics.advance = 24;
+
+ PremultipliedImage rgbaBitmap = impl->drawGlyphBitmap(fontStack, glyphID);
+
+ // Copy alpha values from RGBA bitmap into the AlphaImage output
+ fixedMetrics.bitmap = AlphaImage(size);
+ for (uint32_t i = 0; i < size.width * size.height; i++) {
+ fixedMetrics.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
+ }
+
+ return fixedMetrics;
+}
+
+} // namespace mbgl
diff --git a/platform/android/src/text/local_glyph_rasterizer_jni.hpp b/platform/android/src/text/local_glyph_rasterizer_jni.hpp
new file mode 100644
index 0000000000..38d98d5368
--- /dev/null
+++ b/platform/android/src/text/local_glyph_rasterizer_jni.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+
+#include <jni/jni.hpp>
+
+/*
+ android::LocalGlyphRasterizer is the JNI wrapper of
+ com/mapbox/mapboxsdk/text/LocalGlyphRasterizer
+
+ mbgl::LocalGlyphRasterizer is the portable interface
+ Both implementations are in local_glyph_rasterizer.cpp
+ */
+
+namespace mbgl {
+namespace android {
+
+class LocalGlyphRasterizer {
+public:
+ static PremultipliedImage drawGlyphBitmap(const std::string& fontFamily, const bool bold, const char16_t glyphID);
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/text/LocalGlyphRasterizer"; };
+
+ static jni::Class<LocalGlyphRasterizer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+};
+
+} // namespace android
+} // namespace mbgl