16 constexpr float k_ft_scale = 1.0F / 64.0F;
36 const glm::vec2 m01 = (p0 + p1) * 0.5F;
37 const glm::vec2 m12 = (p1 + p2) * 0.5F;
38 const glm::vec2 mid = (m01 + m12) * 0.5F;
40 const glm::vec2 d = p2 - p0;
41 const glm::vec2 perp { -d.y, d.x };
42 const float dist_sq = (mid.x - p0.x) * perp.x + (mid.y - p0.y) * perp.y;
44 if (dist_sq * dist_sq <= tol_sq * (perp.x * perp.x + perp.y * perp.y)) {
45 ctx.points->push_back(p2);
50 subdivide_conic(ctx, p0, m01, mid, tol_sq);
51 subdivide_conic(ctx, mid, m12, p2, tol_sq);
62 const glm::vec2 m01 = (p0 + p1) * 0.5F;
63 const glm::vec2 m12 = (p1 + p2) * 0.5F;
64 const glm::vec2 m23 = (p2 + p3) * 0.5F;
65 const glm::vec2 m012 = (m01 + m12) * 0.5F;
66 const glm::vec2 m123 = (m12 + m23) * 0.5F;
67 const glm::vec2 mid = (m012 + m123) * 0.5F;
69 const glm::vec2 d = p3 - p0;
70 const glm::vec2 perp { -d.y, d.x };
71 const float denom = perp.x * perp.x + perp.y * perp.y;
73 auto dist_sq = [&](glm::vec2 p) {
74 const float v = (p.x - p0.x) * perp.x + (p.y - p0.y) * perp.y;
78 if (dist_sq(p1) <= tol_sq * denom && dist_sq(p2) <= tol_sq * denom) {
79 ctx.points->push_back(p3);
84 subdivide_cubic(ctx, p0, m01, m012, mid, tol_sq);
85 subdivide_cubic(ctx, mid, m123, m23, p3, tol_sq);
92 int cb_move_to(
const FT_Vector* to,
void* user)
94 auto* ctx =
static_cast<DecomposeCtx*
>(user);
95 if (!ctx->points->empty())
96 ctx->contour_ends->push_back(
static_cast<uint32_t
>(ctx->points->size()));
99 static_cast<float>(to->x) * k_ft_scale,
100 -
static_cast<float>(to->y) * k_ft_scale
102 ctx->points->push_back(ctx->current);
106 int cb_line_to(
const FT_Vector* to,
void* user)
108 auto* ctx =
static_cast<DecomposeCtx*
>(user);
110 static_cast<float>(to->x) * k_ft_scale,
111 -
static_cast<float>(to->y) * k_ft_scale
113 ctx->points->push_back(ctx->current);
117 int cb_conic_to(
const FT_Vector* ctrl,
const FT_Vector* to,
void* user)
119 auto* ctx =
static_cast<DecomposeCtx*
>(user);
121 static_cast<float>(ctrl->x) * k_ft_scale,
122 -
static_cast<float>(ctrl->y) * k_ft_scale
125 static_cast<float>(to->x) * k_ft_scale,
126 -
static_cast<float>(to->y) * k_ft_scale
128 const float tol_sq = ctx->tolerance * ctx->tolerance;
129 subdivide_conic(*ctx, ctx->current, p1, p2, tol_sq);
134 const FT_Vector* ctrl1,
135 const FT_Vector* ctrl2,
139 auto* ctx =
static_cast<DecomposeCtx*
>(user);
141 static_cast<float>(ctrl1->x) * k_ft_scale,
142 -
static_cast<float>(ctrl1->y) * k_ft_scale
145 static_cast<float>(ctrl2->x) * k_ft_scale,
146 -
static_cast<float>(ctrl2->y) * k_ft_scale
149 static_cast<float>(to->x) * k_ft_scale,
150 -
static_cast<float>(to->y) * k_ft_scale
152 const float tol_sq = ctx->tolerance * ctx->tolerance;
153 subdivide_cubic(*ctx, ctx->current, p1, p2, p3, tol_sq);
157 constexpr FT_Outline_Funcs k_funcs {
158 .move_to = cb_move_to,
159 .line_to = cb_line_to,
160 .conic_to = cb_conic_to,
161 .cubic_to = cb_cubic_to,
179 "decompose_glyph: FontFace not loaded");
185 if (
const FT_Error err = FT_Set_Pixel_Sizes(ft, 0, pixel_size); err != 0) {
187 "decompose_glyph: FT_Set_Pixel_Sizes({}) failed: {}", pixel_size,
static_cast<int>(err));
191 const FT_UInt idx = FT_Get_Char_Index(ft,
static_cast<FT_ULong
>(codepoint));
194 "decompose_glyph: no glyph for U+{:04X}", codepoint);
198 if (
const FT_Error err = FT_Load_Glyph(ft, idx, FT_LOAD_NO_BITMAP); err != 0) {
200 "decompose_glyph: FT_Load_Glyph({}) failed: {}", idx,
static_cast<int>(err));
204 result.
advance_x =
static_cast<int32_t
>(ft->glyph->advance.x >> 6);
206 if (ft->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
217 if (
const FT_Error err = FT_Outline_Decompose(&ft->glyph->outline, &k_funcs, &ctx); err != 0) {
219 "decompose_glyph: FT_Outline_Decompose failed: {}",
static_cast<int>(err));
225 if (!result.
points.empty())
236 "decompose_glyph: no default font set");
242 FontFace* face = foundry.get_default_face();
245 "decompose_glyph: default face is null");
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
std::vector< uint32_t > * contour_ends
std::vector< glm::vec2 > * points
FT_Face get_face() const
Raw FT_Face handle for use by GlyphAtlas.
bool is_loaded() const
Returns true after a successful load() call.
Owns a single FT_Face loaded from a file path.
GlyphAtlas * get_default_glyph_atlas() const
Return the default GlyphAtlas, or nullptr if set_default_font() has not been called successfully.
static TypeFaceFoundry & instance()
@ API
API calls from external code.
@ Portal
High-level user-facing API layer.
GlyphOutline decompose_glyph(FontFace &face, uint32_t codepoint, uint32_t pixel_size, float tolerance)
Decompose a Unicode codepoint into a tessellated polyline outline.
std::vector< glm::vec2 > points
std::vector< uint32_t > contour_ends
Vector outline for a single glyph as a flat polyline sequence.