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