[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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
126
platform/android/src/text/local_glyph_rasterizer.cpp
Normal file
126
platform/android/src/text/local_glyph_rasterizer.cpp
Normal file
@@ -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
|
||||
31
platform/android/src/text/local_glyph_rasterizer_jni.hpp
Normal file
31
platform/android/src/text/local_glyph_rasterizer_jni.hpp
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user