Factor out glyph rect function

pull/8738/head
Daniel Wennberg 2025-09-18 14:01:00 -07:00
parent 8fe9c579ef
commit 333a32208e
1 changed files with 39 additions and 81 deletions

View File

@ -385,6 +385,34 @@ pub const Face = struct {
};
}
/// Get a rect that represents the position and size of the loaded glyph.
fn getGlyphSize(glyph: freetype.c.FT_GlyphSlot) font.face.RenderOptions.Constraint.GlyphSize {
// If we're dealing with an outline glyph then we get the
// outline's bounding box instead of using the built-in
// metrics, since that's more precise and allows better
// cell-fitting.
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
// Get the glyph's bounding box before we transform it at all.
// We use this rather than the metrics, since it's more precise.
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox);
return .{
.x = f26dot6ToF64(bbox.xMin),
.y = f26dot6ToF64(bbox.yMin),
.width = f26dot6ToF64(bbox.xMax - bbox.xMin),
.height = f26dot6ToF64(bbox.yMax - bbox.yMin),
};
}
return .{
.x = f26dot6ToF64(glyph.*.metrics.horiBearingX),
.y = f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height),
.width = f26dot6ToF64(glyph.*.metrics.width),
.height = f26dot6ToF64(glyph.*.metrics.height),
};
}
/// Render a glyph using the glyph index. The rendered glyph is stored in the
/// given texture atlas.
pub fn renderGlyph(
@ -403,37 +431,7 @@ pub const Face = struct {
// We get a rect that represents the position
// and size of the glyph before any changes.
const rect: struct {
x: f64,
y: f64,
width: f64,
height: f64,
} = metrics: {
// If we're dealing with an outline glyph then we get the
// outline's bounding box instead of using the built-in
// metrics, since that's more precise and allows better
// cell-fitting.
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
// Get the glyph's bounding box before we transform it at all.
// We use this rather than the metrics, since it's more precise.
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox);
break :metrics .{
.x = f26dot6ToF64(bbox.xMin),
.y = f26dot6ToF64(bbox.yMin),
.width = f26dot6ToF64(bbox.xMax - bbox.xMin),
.height = f26dot6ToF64(bbox.yMax - bbox.yMin),
};
}
break :metrics .{
.x = f26dot6ToF64(glyph.*.metrics.horiBearingX),
.y = f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height),
.width = f26dot6ToF64(glyph.*.metrics.width),
.height = f26dot6ToF64(glyph.*.metrics.height),
};
};
const rect = getGlyphSize(glyph);
// If our glyph is smaller than a quarter pixel in either axis
// then it has no outlines or they're too small to render.
@ -988,21 +986,9 @@ pub const Face = struct {
f26dot6ToF64(glyph.*.advance.x),
max,
);
// We use the outline's bbox instead of the built-in
// metrics for better accuracy (see renderGlyph()).
const ymin, const ymax = metrics: {
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox);
break :metrics .{ bbox.yMin, bbox.yMax };
}
break :metrics .{
glyph.*.metrics.horiBearingY - glyph.*.metrics.height,
glyph.*.metrics.horiBearingY,
};
};
top = @max(f26dot6ToF64(ymax), top);
bottom = @min(f26dot6ToF64(ymin), bottom);
const rect = getGlyphSize(glyph);
top = @max(rect.y + rect.height, top);
bottom = @min(rect.y, bottom);
} else |_| {}
}
}
@ -1042,15 +1028,7 @@ pub const Face = struct {
defer self.ft_mutex.unlock();
if (face.getCharIndex('H')) |glyph_index| {
if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) {
const glyph = face.handle.*.glyph;
// We use the outline's bbox instead of the built-in
// metrics for better accuracy (see renderGlyph()).
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox);
break :cap f26dot6ToF64(bbox.yMax - bbox.yMin);
}
break :cap f26dot6ToF64(glyph.*.metrics.height);
break :cap getGlyphSize(face.handle.*.glyph).height;
} else |_| {}
}
break :cap null;
@ -1060,15 +1038,7 @@ pub const Face = struct {
defer self.ft_mutex.unlock();
if (face.getCharIndex('x')) |glyph_index| {
if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) {
const glyph = face.handle.*.glyph;
// We use the outline's bbox instead of the built-in
// metrics for better accuracy (see renderGlyph()).
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox);
break :ex f26dot6ToF64(bbox.yMax - bbox.yMin);
}
break :ex f26dot6ToF64(glyph.*.metrics.height);
break :ex getGlyphSize(face.handle.*.glyph).height;
} else |_| {}
}
break :ex null;
@ -1095,31 +1065,19 @@ pub const Face = struct {
// This can sometimes happen if there's a CJK font that has been
// patched with the nerd fonts patcher and it butchers the advance
// values so the advance ends up half the width of the actual glyph.
const ft_glyph_width = ft_glyph_width: {
// We use the outline's bbox instead of the built-in
// metrics for better accuracy (see renderGlyph()).
if (ft_glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) {
var bbox: freetype.c.FT_BBox = undefined;
_ = freetype.c.FT_Outline_Get_BBox(&ft_glyph.*.outline, &bbox);
break :ft_glyph_width bbox.xMax - bbox.xMin;
}
break :ft_glyph_width ft_glyph.*.metrics.width;
};
if (ft_glyph_width > ft_glyph.*.advance.x) {
const ft_glyph_width = getGlyphSize(ft_glyph).width;
const advance = f26dot6ToF64(ft_glyph.*.advance.x);
if (ft_glyph_width > advance) {
var buf: [1024]u8 = undefined;
const font_name = self.name(&buf) catch "<Error getting font name>";
log.warn(
"(getMetrics) Width of glyph '水' for font \"{s}\" is greater than its advance ({d} > {d}), discarding ic_width metric.",
.{
font_name,
f26dot6ToF64(ft_glyph_width),
f26dot6ToF64(ft_glyph.*.advance.x),
},
.{ font_name, ft_glyph_width, advance },
);
break :ic_width null;
}
break :ic_width f26dot6ToF64(ft_glyph.*.advance.x);
break :ic_width advance;
};
return .{