summaryrefslogtreecommitdiff
path: root/platform/android/src/text/local_glyph_rasterizer.cpp
blob: 0627eb109ef7007c57dc3facaa8da433a99def95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#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 {

LocalGlyphRasterizer::LocalGlyphRasterizer() {
    UniqueEnv env { AttachEnv() };

    static auto& javaClass = jni::Class<LocalGlyphRasterizer>::Singleton(*env);
    static auto constructor = javaClass.GetConstructor(*env);

    javaObject = jni::NewGlobal(*env, javaClass.New(*env, constructor));
}

PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& fontFamily, const bool bold, const GlyphID glyphID) {
    UniqueEnv env { AttachEnv() };

    static auto& javaClass = jni::Class<LocalGlyphRasterizer>::Singleton(*env);
    static auto drawGlyphBitmap = javaClass.GetMethod<jni::Object<Bitmap> (jni::String, jni::jboolean, jni::jchar)>(*env, "drawGlyphBitmap");

    return Bitmap::GetImage(*env,
        javaObject.Call(*env,
            drawGlyphBitmap,
            jni::Make<jni::String>(*env, fontFamily),
            static_cast<jni::jboolean>(bold),
            static_cast<jni::jchar>(glyphID)));
}

void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) {
    jni::Class<LocalGlyphRasterizer>::Singleton(env);
}

} // 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;
                break;
            }
        }
        return androidLocalGlyphRasterizer.drawGlyphBitmap(*fontFamily, bold, glyphID);
    }

private:
    optional<std::string> fontFamily;
    android::LocalGlyphRasterizer androidLocalGlyphRasterizer;
};

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