summaryrefslogtreecommitdiff
path: root/platform/android/src/text/local_glyph_rasterizer.cpp
blob: 8892ee3f37700ef707175f388ce50c1b25efa795 (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
130
#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