#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 { LocalGlyphRasterizer::LocalGlyphRasterizer() { UniqueEnv env { AttachEnv() }; static auto javaClass = jni::Class::Singleton(*env); static auto constructor = javaClass.GetConstructor(*env); javaObject = javaClass.New(*env, constructor).NewGlobalRef(*env); } PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& fontFamily, const bool bold, const GlyphID glyphID) { UniqueEnv env { AttachEnv() }; static auto javaClass = jni::Class::Singleton(*env); static auto drawGlyphBitmap = javaClass.GetMethod (jni::String, jni::jboolean, jni::jchar)>(*env, "drawGlyphBitmap"); auto javaBitmap = jni::SeizeLocal(*env, javaObject->Call(*env, drawGlyphBitmap, *jni::SeizeLocal(*env, jni::Make(*env, fontFamily)), static_cast(bold), static_cast(glyphID))); return Bitmap::GetImage(*env, *javaBitmap); } void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) { jni::Class::Singleton(env); } } // 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; break; } } return androidLocalGlyphRasterizer.drawGlyphBitmap(*fontFamily, bold, glyphID); } private: optional fontFamily; android::LocalGlyphRasterizer androidLocalGlyphRasterizer; }; 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