From 00ed34f7d700ae255eae1af1ca46b939ffee219d Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 30 Nov 2017 12:38:06 -0800 Subject: [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 --- .../mapboxsdk/text/LocalGlyphRasterizer.java | 46 ++++++++ platform/android/config.cmake | 3 +- platform/android/src/jni.cpp | 4 + .../android/src/text/local_glyph_rasterizer.cpp | 126 +++++++++++++++++++++ .../src/text/local_glyph_rasterizer_jni.hpp | 31 +++++ 5 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java create mode 100644 platform/android/src/text/local_glyph_rasterizer.cpp create mode 100644 platform/android/src/text/local_glyph_rasterizer_jni.hpp 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 +#include +#include + +#include + +#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(jni::String, jni::jboolean, jni::jchar); + auto method = javaClass.GetStaticMethod(*env, "drawGlyphBitmap"); + + jni::String jniFontFamily = jni::Make(*env, fontFamily); + + auto javaBitmap = javaClass.Call(*env, + method, + jniFontFamily, + static_cast(bold), + static_cast(glyphID)); + + PremultipliedImage result = Bitmap::GetImage(*env, javaBitmap); + jni::DeleteLocalRef(*env, javaBitmap); + return result; +} + +void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); +} + +jni::Class LocalGlyphRasterizer::javaClass; + +} // namespace android + +class LocalGlyphRasterizer::Impl { +public: + Impl(const optional 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 fontFamily; +}; + +LocalGlyphRasterizer::LocalGlyphRasterizer(const optional fontFamily) + : impl(std::make_unique(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 + +#include + +/* + 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 javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + +} // namespace android +} // namespace mbgl -- cgit v1.2.1