Skip to content

Commit 09d395d

Browse files
Add HarfBuzz Text Shaping and Font Fallback Support (#3611)
Co-authored-by: alan wei chen <[email protected]>
1 parent 6026434 commit 09d395d

File tree

82 files changed

+27006
-151
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+27006
-151
lines changed

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
[submodule "platform/windows/vendor/vcpkg"]
3838
path = platform/windows/vendor/vcpkg
3939
url = https://github.com/microsoft/vcpkg.git
40+
[submodule "vendor/harfbuzzFreetype/freetype"]
41+
path = vendor/freetype
42+
url=https://github.com/freetype/freetype.git
43+
[submodule "vendor/harfbuzzFreetype/harfbuzz"]
44+
path = vendor/harfbuzz
45+
url=https://github.com/harfbuzz/harfbuzz
4046
[submodule "vendor/boost"]
4147
path = vendor/boost
4248
url = https://github.com/maplibre/maplibre-gl-native-boost.git

BUILD.bazel

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ load(
2222
"MLN_OPENGL_SOURCE",
2323
"MLN_PRIVATE_GENERATED_STYLE_HEADERS",
2424
"MLN_PUBLIC_GENERATED_STYLE_HEADERS",
25+
"MLN_SHAPING_HARFBUZZ_SRCS",
2526
)
2627
load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS")
2728

@@ -85,6 +86,9 @@ cc_library(
8586
":drawable_renderer": MLN_OPENGL_SOURCE + MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_GL_SOURCE,
8687
":metal_renderer": MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_MTL_SOURCE,
8788
"//conditions:default": [],
89+
}) + select({
90+
"//:harfbuzz_text_shaping": MLN_SHAPING_HARFBUZZ_SRCS,
91+
"//conditions:default": [],
8892
}),
8993
hdrs = MLN_LAYER_PLUGIN_HEADERS + MLN_CORE_HEADERS + select({
9094
":drawable_renderer": MLN_OPENGL_HEADERS + MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_GL_HEADERS,
@@ -102,6 +106,9 @@ cc_library(
102106
}) + select({
103107
"@platforms//os:ios": ["GLES_SILENCE_DEPRECATION=1"],
104108
"//conditions:default": [],
109+
}) + select({
110+
"//:harfbuzz_text_shaping": ["MLN_TEXT_SHAPING_HARFBUZZ=1"],
111+
"//conditions:default": [],
105112
}),
106113
includes = [
107114
"include",
@@ -111,6 +118,12 @@ cc_library(
111118
"vendor/metal-cpp",
112119
],
113120
"//conditions:default": [],
121+
}) + select({
122+
"//:harfbuzz_text_shaping": [
123+
"vendor/freetype/include",
124+
"vendor/harfbuzz/src",
125+
],
126+
"//conditions:default": [],
114127
}),
115128
local_defines = [
116129
r"MLN_VERSION_REV=\"standalone\"",
@@ -147,6 +160,12 @@ cc_library(
147160
"//vendor:metal-cpp",
148161
],
149162
"//conditions:default": [],
163+
}) + select({
164+
":harfbuzz_text_shaping": [
165+
"//vendor:freetype",
166+
"//vendor:harfbuzz",
167+
],
168+
"//conditions:default": [],
150169
}) + select({
151170
":rust": [
152171
"//rustutils:rustutilslib",
@@ -182,6 +201,31 @@ config_setting(
182201
},
183202
)
184203

204+
# Selects the shaping implementation to utilize in the core
205+
206+
string_flag(
207+
name = "shaping",
208+
build_setting_default = "harfbuzz",
209+
values = [
210+
"legacy",
211+
"harfbuzz",
212+
],
213+
)
214+
215+
config_setting(
216+
name = "harfbuzz_text_shaping",
217+
flag_values = {
218+
":shaping": "harfbuzz",
219+
},
220+
)
221+
222+
config_setting(
223+
name = "legacy_text_shaping",
224+
flag_values = {
225+
":shaping": "legacy",
226+
},
227+
)
228+
185229
bool_flag(
186230
name = "use_rust",
187231
build_setting_default = False,

CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ option(MLN_WITH_WERROR "Make all compilation warnings errors" ON)
1616
option(MLN_USE_UNORDERED_DENSE "Use ankerl dense containers for performance" ON)
1717
option(MLN_USE_TRACY "Enable Tracy instrumentation" OFF)
1818
option(MLN_USE_RUST "Use components in Rust" OFF)
19+
option(MLN_TEXT_SHAPING_HARFBUZZ "Use haffbuzz to shape complex text" ON)
1920
option(MLN_CORE_INCLUDE_DEPS "Include depdendencies in static build of core" OFF)
2021
option(MLN_CREATE_AUTORELEASEPOOL "Create autoreleasepool in render loop" OFF)
2122

@@ -1333,6 +1334,35 @@ list(APPEND
13331334
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/custom_drawable_layer.cpp
13341335
)
13351336

1337+
# Harfbuzz text shaping
1338+
set(SHAPE_TARGETS, "")
1339+
list(APPEND
1340+
SRC_FILES
1341+
# Base text shaping files, which are always included. They don't depend on HarfBuzz if MLN_TEXT_SHAPING_HARFBUZZ is OFF.
1342+
${PROJECT_SOURCE_DIR}/src/mbgl/text/harfbuzz.cpp
1343+
${PROJECT_SOURCE_DIR}/src/mbgl/text/harfbuzz.hpp
1344+
)
1345+
if (MLN_TEXT_SHAPING_HARFBUZZ)
1346+
message(STATUS "Configuring with HarfBuzz text shaping")
1347+
list(APPEND
1348+
SRC_FILES
1349+
${PROJECT_SOURCE_DIR}/src/mbgl/text/harfbuzz_impl.cpp
1350+
${PROJECT_SOURCE_DIR}/src/mbgl/text/harfbuzz_impl.hpp
1351+
${PROJECT_SOURCE_DIR}/src/mbgl/text/freetype.cpp
1352+
${PROJECT_SOURCE_DIR}/src/mbgl/text/freetype.hpp
1353+
)
1354+
target_compile_definitions(
1355+
mbgl-core
1356+
PRIVATE MLN_TEXT_SHAPING_HARFBUZZ=1
1357+
)
1358+
include(${PROJECT_SOURCE_DIR}/vendor/freetype.cmake)
1359+
include(${PROJECT_SOURCE_DIR}/vendor/harfbuzz.cmake)
1360+
set(SHAPE_TARGETS
1361+
freetype
1362+
harfbuzz
1363+
)
1364+
endif()
1365+
13361366
target_sources(
13371367
mbgl-core PRIVATE
13381368
${INCLUDE_FILES}
@@ -1436,6 +1466,7 @@ target_link_libraries(
14361466
mbgl-vendor-wagyu
14371467
$<$<BOOL:${MLN_WITH_METAL}>:mbgl-vendor-metal-cpp>
14381468
$<IF:$<BOOL:${MLN_USE_RUST}>,mbgl-rustutils,mbgl-vendor-csscolorparser>
1469+
${SHAPE_TARGETS} # from harfbuzz.cmake
14391470
PUBLIC
14401471
MapLibreNative::Base
14411472
MapLibreNative::Base::Extras::expected-lite
@@ -1476,6 +1507,7 @@ set(EXPORT_TARGETS
14761507
mbgl-vendor-wagyu
14771508
mbgl-vendor-metal-cpp
14781509
unordered_dense
1510+
${SHAPE_TARGETS} # from harfbuzz.cmake
14791511
)
14801512

14811513
if(MLN_USE_RUST)

bazel/core.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ MLN_CORE_SOURCE = [
489489
"src/mbgl/text/shaping.hpp",
490490
"src/mbgl/text/tagged_string.cpp",
491491
"src/mbgl/text/tagged_string.hpp",
492+
"src/mbgl/text/harfbuzz.cpp",
493+
"src/mbgl/text/harfbuzz.hpp",
492494
"src/mbgl/tile/custom_geometry_tile.cpp",
493495
"src/mbgl/tile/custom_geometry_tile.hpp",
494496
"src/mbgl/tile/geojson_tile.cpp",
@@ -1096,3 +1098,10 @@ MLN_DRAWABLES_MTL_HEADERS = [
10961098
"include/mbgl/style/layers/mtl/custom_layer_render_parameters.hpp",
10971099
"include/mbgl/shaders/mtl/widevector.hpp",
10981100
]
1101+
1102+
MLN_SHAPING_HARFBUZZ_SRCS = [
1103+
"src/mbgl/text/freetype.hpp",
1104+
"src/mbgl/text/freetype.cpp",
1105+
"src/mbgl/text/harfbuzz_impl.hpp",
1106+
"src/mbgl/text/harfbuzz_impl.cpp",
1107+
]

include/mbgl/storage/resource.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class Resource {
8282
static Resource glyphs(const std::string& urlTemplate,
8383
const FontStack& fontStack,
8484
const std::pair<uint16_t, uint16_t>& glyphRange);
85+
static Resource fontFace(const std::string& url);
8586
static Resource spriteImage(const std::string& base, float pixelRatio);
8687
static Resource spriteJSON(const std::string& base, float pixelRatio);
8788
static Resource image(const std::string& url);

include/mbgl/text/glyph.hpp

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,40 @@
1818

1919
namespace mbgl {
2020

21-
using GlyphID = char16_t;
21+
union GlyphID {
22+
char32_t hash;
23+
struct {
24+
char16_t code;
25+
GlyphIDType type;
26+
} complex;
27+
28+
GlyphID(int codepoint) {
29+
complex.type = FontPBF;
30+
complex.code = codepoint;
31+
}
32+
GlyphID(uint32_t codepoint) {
33+
complex.type = FontPBF;
34+
complex.code = codepoint;
35+
}
36+
GlyphID(char16_t codepoint) {
37+
complex.type = FontPBF;
38+
complex.code = codepoint;
39+
}
40+
41+
GlyphID(char16_t index, GlyphIDType t) {
42+
complex.type = t;
43+
complex.code = index;
44+
}
45+
46+
operator char16_t() { return complex.code; }
47+
operator char32_t() { return hash; }
48+
bool operator<(const GlyphID &other) const { return hash < other.hash; }
49+
bool operator>(const GlyphID &other) const { return hash > other.hash; }
50+
51+
bool operator<(const uint16_t &other) const { return hash < other; }
52+
bool operator>(const uint16_t &other) const { return hash > other; }
53+
};
54+
2255
using GlyphIDs = std::set<GlyphID>;
2356

2457
// Note: this only works for the BMP
@@ -32,7 +65,7 @@ struct GlyphMetrics {
3265
uint32_t advance = 0;
3366
};
3467

35-
inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) {
68+
inline bool operator==(const GlyphMetrics &lhs, const GlyphMetrics &rhs) {
3669
return lhs.width == rhs.width && lhs.height == rhs.height && lhs.left == rhs.left && lhs.top == rhs.top &&
3770
lhs.advance == rhs.advance;
3871
}
@@ -114,7 +147,7 @@ class Shaping {
114147
float right = 0;
115148
WritingModeType writingMode;
116149
explicit operator bool() const {
117-
return std::ranges::any_of(positionedLines, [](const auto& line) { return !line.positionedGlyphs.empty(); });
150+
return std::ranges::any_of(positionedLines, [](const auto &line) { return !line.positionedGlyphs.empty(); });
118151
}
119152
// The y offset *should* be part of the font metadata.
120153
static constexpr int32_t yOffset = -17;
@@ -128,7 +161,50 @@ enum class WritingModeType : uint8_t {
128161
Vertical = 1 << 1,
129162
};
130163

131-
using GlyphDependencies = std::map<FontStack, GlyphIDs>;
164+
// style defined faces
165+
struct FontFace {
166+
using Range = std::pair<uint32_t, uint32_t>;
167+
GlyphIDType type; // an unique glyph id
168+
std::string name; // font face name
169+
std::string url; // font file url
170+
std::vector<Range> ranges; // unicode ranges
171+
172+
FontFace() = default;
173+
FontFace(const std::string &name_, const std::string &url_, const std::vector<Range> &ranges_)
174+
: type(FontPBF),
175+
name(name_),
176+
url(url_),
177+
ranges(ranges_) {}
178+
179+
FontFace(const std::string &name_, const std::string &url_, std::vector<Range> &&ranges_)
180+
: type(FontPBF),
181+
name(name_),
182+
url(url_),
183+
ranges(std::move(ranges_)) {}
184+
185+
auto valid() const -> bool { return !name.empty() && !url.empty() && !ranges.empty(); }
186+
};
187+
188+
using FontFaces = std::vector<FontFace>;
189+
190+
struct HBShapeRequest {
191+
std::u16string str;
192+
FontStack fontStack;
193+
GlyphIDType type;
194+
195+
HBShapeRequest(const std::u16string &str_, const FontStack &fontStack_, GlyphIDType type_)
196+
: str(str_),
197+
fontStack(fontStack_),
198+
type(type_) {}
199+
};
200+
201+
using HBShapeRequests = std::map<FontStack, std::map<GlyphIDType, std::set<std::u16string>>>;
202+
203+
struct GlyphDependencies {
204+
std::map<FontStack, GlyphIDs> glyphs;
205+
HBShapeRequests shapes;
206+
};
207+
132208
using GlyphRangeDependencies = std::map<FontStack, std::unordered_set<GlyphRange>>;
133209

134210
struct GlyphPosition {

include/mbgl/text/glyph_range.hpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,30 @@
44
#include <cstdint>
55
#include <unordered_set>
66
#include <mbgl/util/hash.hpp>
7+
#include <mbgl/util/font_stack.hpp>
78

89
namespace mbgl {
910

10-
using GlyphRange = std::pair<uint16_t, uint16_t>;
11+
enum GlyphIDType : uint16_t {
12+
FontPBF = 0x00,
13+
};
14+
15+
GlyphIDType genNewGlyphIDType(const std::string &url,
16+
const FontStack &fontStack,
17+
const std::vector<std::pair<uint32_t, uint32_t>> &pairs);
18+
19+
class GlyphRange {
20+
public:
21+
uint16_t first = 0;
22+
uint16_t second = 0;
23+
24+
GlyphIDType type = GlyphIDType::FontPBF;
25+
26+
GlyphRange(uint32_t first_, uint32_t second_, GlyphIDType type_ = FontPBF);
27+
28+
bool operator==(const GlyphRange &other) const;
29+
bool operator<(const GlyphRange &other) const;
30+
};
1131

1232
constexpr uint32_t GLYPHS_PER_GLYPH_RANGE = 256;
1333
constexpr uint32_t GLYPH_RANGES_PER_FONT_STACK = 256;
@@ -20,7 +40,7 @@ namespace std {
2040

2141
template <>
2242
struct hash<mbgl::GlyphRange> {
23-
std::size_t operator()(const mbgl::GlyphRange& range) const { return mbgl::util::hash(range.first, range.second); }
43+
std::size_t operator()(const mbgl::GlyphRange &range) const { return mbgl::util::hash(range.first, range.second); }
2444
};
2545

2646
} // namespace std

0 commit comments

Comments
 (0)