11 switch (utf8proc_category(
static_cast<utf8proc_int32_t
>(codepoint))) {
12 case UTF8PROC_CATEGORY_CC:
13 case UTF8PROC_CATEGORY_CF:
14 case UTF8PROC_CATEGORY_CS:
15 case UTF8PROC_CATEGORY_CO:
23 std::string_view text,
31 return { .
quads = {}, .final_pen_x = pen_x, .final_pen_y = pen_y };
37 const float origin_x = 0.F;
40 const auto* prewarm_bytes =
reinterpret_cast<const utf8proc_uint8_t*
>(text.data());
41 auto prewarm_remaining =
static_cast<utf8proc_ssize_t
>(text.size());
42 utf8proc_ssize_t prewarm_offset = 0;
44 while (prewarm_offset < prewarm_remaining) {
45 utf8proc_int32_t prewarm_codepoint = 0;
46 const utf8proc_ssize_t n = utf8proc_iterate(
47 prewarm_bytes + prewarm_offset, prewarm_remaining - prewarm_offset, &prewarm_codepoint);
55 if (prewarm_codepoint >= 0 && !
is_control(
static_cast<uint32_t
>(prewarm_codepoint))) {
65 FT_UInt prev_glyph_index = 0;
68 constexpr float k_wrap_margin = 1.5F;
70 const auto apply_wrap = [&](
float upcoming_width) ->
bool {
71 if (wrap_w > 0 && pen_x + upcoming_width * k_wrap_margin >
static_cast<float>(wrap_w)) {
81 const auto* bytes =
reinterpret_cast<const utf8proc_uint8_t*
>(text.data());
82 auto remaining =
static_cast<utf8proc_ssize_t
>(text.size());
83 utf8proc_ssize_t
offset = 0;
85 while (
offset < remaining) {
86 const auto codepoint_start =
static_cast<size_t>(
offset);
87 utf8proc_int32_t codepoint = 0;
88 const utf8proc_ssize_t n = utf8proc_iterate(bytes +
offset, remaining -
offset, &codepoint);
92 "TypeSetter: invalid UTF-8 sequence at byte offset {}, skipping byte",
93 static_cast<size_t>(
offset));
104 if (
is_control(
static_cast<uint32_t
>(codepoint))) {
105 if (codepoint ==
'\t' && tab_width > 0) {
107 const float stop_width = space && space->
advance_x > 0
108 ?
static_cast<float>(space->
advance_x) *
static_cast<float>(tab_width)
111 if (stop_width > 0.F) {
112 pen_x = (std::floor((pen_x - origin_x) / stop_width) + 1.F) * stop_width + origin_x;
115 }
else if (codepoint ==
'\n') {
120 prev_glyph_index = 0;
127 prev_glyph_index = 0;
132 apply_wrap(
static_cast<float>(m->
advance_x));
134 if (prev_face == m->
face && prev_glyph_index != 0 && m->
glyph_index != 0
138 pen_x +=
static_cast<float>(delta.x >> 6);
144 q.y0 = pen_y -
static_cast<float>(m->
bearing_y);
145 q.x1 =
q.x0 +
static_cast<float>(m->
width);
146 q.y1 =
q.y0 +
static_cast<float>(m->
height);
150 q.codepoint =
static_cast<uint32_t
>(codepoint);
152 q.byte_offset = codepoint_start;
153 out.quads.push_back(
q);
156 pen_x +=
static_cast<float>(m->
advance_x);
161 out.final_pen_x = pen_x;
162 out.final_pen_y = pen_y;
168 if (!utf8proc_codepoint_valid(
static_cast<utf8proc_int32_t
>(codepoint))) {
172 std::array<utf8proc_uint8_t, 4> buf {};
173 const utf8proc_ssize_t n = utf8proc_encode_char(
174 static_cast<utf8proc_int32_t
>(codepoint), buf.data());
180 return {
reinterpret_cast<const char*
>(buf.data()),
static_cast<size_t>(n) };
185 if (byte_offset >= text.size()) {
189 const auto* bytes =
reinterpret_cast<const utf8proc_uint8_t*
>(text.data());
190 const auto remaining =
static_cast<utf8proc_ssize_t
>(text.size() - byte_offset);
192 utf8proc_int32_t codepoint = 0;
193 const utf8proc_ssize_t n = utf8proc_iterate(bytes + byte_offset, remaining, &codepoint);
195 return byte_offset +
static_cast<size_t>(n > 0 ? n : 1);
200 if (byte_offset == 0) {
204 size_t pos = byte_offset - 1;
205 constexpr size_t max_continuation_bytes = 3;
208 while (pos > 0 && steps < max_continuation_bytes
209 && (
static_cast<uint8_t
>(text[pos]) & 0xC0) == 0x80) {
220 out.
start = std::min(std::min(
a,
b), text.size());
221 out.end = std::min(std::max(
a,
b), text.size());
223 if (
out.start >=
out.end) {
227 out.text = std::string(text.substr(
out.start,
out.end -
out.start));
229 for (
const auto&
q : layout.
quads) {
230 if (
q.byte_offset >=
out.start &&
q.byte_offset <
out.end) {
231 out.quads.push_back(
q);
239 float abs_f(
float v) {
return v < 0.F ? -v : v; }
243 std::string_view text,
253 if (layout.
quads.empty()) {
257 const auto line_h =
static_cast<float>(atlas.
line_height());
259 float line_y = layout.
quads.front().y0;
260 float best_line_dy = abs_f(line_y - y);
261 for (
const auto&
q : layout.
quads) {
262 const float dy = abs_f(
q.y0 - y);
263 if (dy < best_line_dy) {
272 for (
const auto&
q : layout.
quads) {
273 if (abs_f(
q.y0 - line_y) > line_h * 0.5F) {
277 if (x >=
q.x0 && x <=
q.x1) {
278 return x > (
q.x0 +
q.x1) * 0.5F
282 if (
q.x1 <= x && (!left ||
q.
x1 > left->
x1)) {
285 if (
q.x0 >= x && (!right ||
q.
x0 < right->
x0)) {
290 const GlyphQuad* anchor = left ? left : right;
291 const float pen_line_y =
x_at(text, atlas, anchor->
byte_offset, pen_x, pen_y, wrap_w, tab_width).
y;
293 const auto same_line = [&](
size_t offset) {
294 return x_at(text, atlas,
offset, pen_x, pen_y, wrap_w, tab_width).
y == pen_line_y;
297 size_t bracket_start = 0;
302 while (bracket_start > 0) {
304 if (!same_line(prev)) {
307 bracket_start = prev;
311 size_t bracket_end = text.size();
316 while (bracket_end < text.size()) {
318 if (!same_line(next)) {
325 if (bracket_start >= bracket_end) {
326 return bracket_start;
329 size_t best_offset = bracket_start;
330 float best_dx = abs_f(
x_at(text, atlas, bracket_start, pen_x, pen_y, wrap_w, tab_width).x - x);
332 for (
size_t off = bracket_start; off < bracket_end;) {
334 const float dx = abs_f(
x_at(text, atlas, next, pen_x, pen_y, wrap_w, tab_width).x - x);
346 std::string_view text,
354 const size_t clamped = std::min(byte_offset, text.size());
355 const LayoutResult prefix =
lay_out(text.substr(0, clamped), atlas, pen_x, pen_y, wrap_w, tab_width);
#define MF_WARN(comp, ctx,...)
std::vector< float > * out
FT_Face get_face() const
Raw FT_Face handle for use by GlyphAtlas.
Owns a single FT_Face loaded from a file path.
uint32_t line_height() const
Line advance in pixels for this atlas's pixel_size.
const GlyphMetrics * get_or_rasterize(FT_UInt glyph_index)
Return metrics for a glyph, rasterizing it into the atlas if needed.
Rasterizes and packs glyphs from a FontFace into a TextureContainer.
@ API
API calls from external code.
@ Portal
High-level user-facing API layer.
size_t previous_codepoint_offset(std::string_view text, size_t byte_offset)
Byte offset of the codepoint boundary before byte_offset.
std::string encode_utf8(uint32_t codepoint)
Encode a Unicode codepoint as a UTF-8 byte sequence.
bool is_control(uint32_t codepoint) noexcept
True if a codepoint must never be rasterized or treated as printable content.
size_t next_codepoint_offset(std::string_view text, size_t byte_offset)
Byte offset of the codepoint boundary after byte_offset.
PenPosition x_at(std::string_view text, GlyphAtlas &atlas, size_t byte_offset, float pen_x, float pen_y, uint32_t wrap_w, uint32_t tab_width)
Pen position immediately before byte_offset, in lay_out()'s space.
CopiedText copy(std::string_view text, const LayoutResult &layout, size_t a, size_t b)
Copy a byte range: the text it spans, and the quads that render it.
LayoutResult lay_out(std::string_view text, GlyphAtlas &atlas, float pen_x, float pen_y, uint32_t wrap_w, uint32_t tab_width)
Lay out a UTF-8 string into a sequence of screen-space quads.
size_t index_at(std::string_view text, const LayoutResult &layout, GlyphAtlas &atlas, float x, float y, float pen_x, float pen_y, uint32_t wrap_w, uint32_t tab_width)
Byte offset of the codepoint boundary nearest a screen-space point.
size_t start
Normalized, clamped byte range actually used.
Result of copy(): a byte range's text plus the quads that render it.
FT_UInt glyph_index
Glyph index within face (from FT_Get_Char_Index).
int32_t bearing_x
Horizontal bearing in pixels (from FT_GlyphSlot).
float uv_x0
Left UV edge in atlas texture.
float uv_x1
Right UV edge in atlas texture.
const FontFace * face
Face that produced this glyph (primary or fallback).
uint32_t height
Glyph bitmap height in pixels.
int32_t advance_x
Horizontal advance in pixels (26.6 fixed-point >> 6).
uint32_t width
Glyph bitmap width in pixels.
float uv_y0
Top UV edge in atlas texture.
int32_t bearing_y
Vertical bearing in pixels (from FT_GlyphSlot).
float uv_y1
Bottom UV edge in atlas texture.
Per-glyph layout and UV data produced by GlyphAtlas.
size_t byte_offset
Byte offset of codepoint's first byte in the source text passed to lay_out().
Screen-space quad for one rasterized glyph.
std::vector< GlyphQuad > quads
Result of lay_out(), carrying the quads and the final pen position.
Pen coordinates returned by x_at(), in the same space as GlyphQuad.