#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