mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
font: further improve ul/st position calculations
This commit is contained in:
@ -594,7 +594,10 @@ pub const Face = struct {
|
||||
// All of these metrics are based on our layout above.
|
||||
const cell_height = @ceil(layout_metrics.height);
|
||||
const cell_baseline = @ceil(layout_metrics.height - layout_metrics.ascent);
|
||||
|
||||
const underline_thickness = @ceil(@as(f32, @floatCast(ct_font.getUnderlineThickness())));
|
||||
const strikethrough_thickness = underline_thickness;
|
||||
|
||||
const strikethrough_position = strikethrough_position: {
|
||||
// This is the height of lower case letters in our font.
|
||||
const ex_height = ct_font.getXHeight();
|
||||
@ -608,20 +611,21 @@ pub const Face = struct {
|
||||
// is the distance from the top down to the baseline, then
|
||||
// we subtract half of the ex height to go back up to the
|
||||
// correct height that should evenly split lowercase text.
|
||||
const pos = layout_metrics.ascent - ex_height * 0.5 + 1;
|
||||
const pos = layout_metrics.ascent -
|
||||
ex_height * 0.5 +
|
||||
strikethrough_thickness * 0.5 +
|
||||
1;
|
||||
|
||||
break :strikethrough_position @ceil(pos);
|
||||
};
|
||||
const strikethrough_thickness = underline_thickness;
|
||||
|
||||
// Underline position reported is usually something like "-1" to
|
||||
// represent the amount under the baseline. We add this to our real
|
||||
// baseline to get the actual value from the bottom (+y is up).
|
||||
// The final underline position is +y from the TOP (confusing)
|
||||
// so we have to subtract from the cell height.
|
||||
const underline_position = cell_height -
|
||||
(cell_baseline + @ceil(@as(f32, @floatCast(ct_font.getUnderlinePosition())))) +
|
||||
1;
|
||||
const underline_position = @ceil(layout_metrics.ascent -
|
||||
@as(f32, @floatCast(ct_font.getUnderlinePosition())) + 1);
|
||||
|
||||
// Note: is this useful?
|
||||
// const units_per_em = ct_font.getUnitsPerEm();
|
||||
|
@ -660,52 +660,50 @@ pub const Face = struct {
|
||||
// is reversed.
|
||||
const cell_baseline = -1 * f26dot6ToFloat(size_metrics.descender);
|
||||
|
||||
const underline_thickness = @max(@as(f32, 1), fontUnitsToPxY(
|
||||
face,
|
||||
face.handle.*.underline_thickness,
|
||||
));
|
||||
|
||||
// The underline position. This is a value from the top where the
|
||||
// underline should go.
|
||||
const underline_position: f32 = underline_pos: {
|
||||
// The ascender is already scaled for scalable fonts, but the
|
||||
// underline position is not.
|
||||
const ascender_px = @as(i32, @intCast(size_metrics.ascender)) >> 6;
|
||||
const declared_px = freetype.mulFix(
|
||||
const declared_px = @as(f32, @floatFromInt(freetype.mulFix(
|
||||
face.handle.*.underline_position,
|
||||
@intCast(face.handle.*.size.*.metrics.y_scale),
|
||||
) >> 6;
|
||||
))) / 64;
|
||||
|
||||
// We use the declared underline position if its available
|
||||
const declared = ascender_px - declared_px;
|
||||
const declared = cell_height - cell_baseline - declared_px;
|
||||
if (declared > 0)
|
||||
break :underline_pos @floatFromInt(declared);
|
||||
break :underline_pos declared;
|
||||
|
||||
// If we have no declared underline position, we go slightly under the
|
||||
// cell height (mainly: non-scalable fonts, i.e. emoji)
|
||||
break :underline_pos cell_height - 1;
|
||||
};
|
||||
const underline_thickness = @max(@as(f32, 1), fontUnitsToPxY(
|
||||
face,
|
||||
face.handle.*.underline_thickness,
|
||||
));
|
||||
|
||||
// The strikethrough position. We use the position provided by the
|
||||
// font if it exists otherwise we calculate a best guess.
|
||||
const strikethrough: struct {
|
||||
pos: f32,
|
||||
thickness: f32,
|
||||
} = if (face.getSfntTable(.os2)) |os2| .{
|
||||
.pos = pos: {
|
||||
// Ascender is scaled, strikeout pos is not
|
||||
const ascender_px = @as(i32, @intCast(size_metrics.ascender)) >> 6;
|
||||
const declared_px = freetype.mulFix(
|
||||
} = if (face.getSfntTable(.os2)) |os2| st: {
|
||||
const thickness = @max(@as(f32, 1), fontUnitsToPxY(face, os2.yStrikeoutSize));
|
||||
|
||||
const pos = @as(f32, @floatFromInt(freetype.mulFix(
|
||||
os2.yStrikeoutPosition,
|
||||
@as(i32, @intCast(face.handle.*.size.*.metrics.y_scale)),
|
||||
) >> 6;
|
||||
))) / 64;
|
||||
|
||||
break :pos @floatFromInt(ascender_px - declared_px);
|
||||
},
|
||||
.thickness = @max(@as(f32, 1), fontUnitsToPxY(face, os2.yStrikeoutSize)),
|
||||
break :st .{
|
||||
.pos = @ceil(cell_height - cell_baseline - pos + thickness + 1),
|
||||
.thickness = thickness,
|
||||
};
|
||||
} else .{
|
||||
// Exactly 50% of the ex height so that our strikethrough is
|
||||
// centered through lowercase text. This is a common choice.
|
||||
.pos = cell_baseline - ex_height * 0.5 + 1,
|
||||
.pos = @ceil(cell_height - cell_baseline - ex_height * 0.5 + underline_thickness),
|
||||
.thickness = underline_thickness,
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user