font: use adjusted cell width for recentering again

The old method was nice, but had an issue that's intractible without
significant reworking in how we do shaping: combining glyphs need to
position relative to the glyph they're combining with, but if we re-
center that glyph, it will be off by some amount.
pull/8206/head
Qwerasd 2025-08-10 15:49:54 -06:00
parent 195cbb6a1c
commit 20a9a3a8c2
2 changed files with 39 additions and 37 deletions

View File

@ -388,7 +388,23 @@ pub const Face = struct {
y = @round(y); y = @round(y);
} }
// Our pixel bearings for the final glyph. // If the cell width was adjusted wider, we re-center all glyphs
// in the new width, so that they aren't weirdly off to the left.
if (metrics.original_cell_width) |original| recenter: {
// We don't do this if the constraint has a horizontal alignment,
// since in that case the position was already calculated with the
// new cell width in mind.
if (opts.constraint.align_horizontal != .none) break :recenter;
// If the original width was wider then we don't do anything.
if (original >= metrics.cell_width) break :recenter;
// We add half the difference to re-center.
x += (cell_width - @as(f64, @floatFromInt(original))) / 2;
}
// Our whole-pixel bearings for the final glyph.
// The fractional portion will be included in the rasterized position.
const px_x: i32 = @intFromFloat(@floor(x)); const px_x: i32 = @intFromFloat(@floor(x));
const px_y: i32 = @intFromFloat(@floor(y)); const px_y: i32 = @intFromFloat(@floor(y));

View File

@ -417,6 +417,27 @@ pub const Face = struct {
var x = glyph_size.x; var x = glyph_size.x;
const y = glyph_size.y; const y = glyph_size.y;
// If the cell width was adjusted wider, we re-center all glyphs
// in the new width, so that they aren't weirdly off to the left.
if (metrics.original_cell_width) |original| recenter: {
// We don't do this if the constraint has a horizontal alignment,
// since in that case the position was already calculated with the
// new cell width in mind.
if (opts.constraint.align_horizontal != .none) break :recenter;
// If the original width was wider then we don't do anything.
if (original >= metrics.cell_width) break :recenter;
// We add half the difference to re-center.
//
// NOTE: We round this to a whole-pixel amount because under
// FreeType, the outlines will be hinted, which isn't
// the case under CoreText. If we move the outlines by
// a non-whole-pixel amount, it completely ruins the
// hinting.
x += @round((cell_width - @as(f64, @floatFromInt(original))) / 2);
}
// Now we can render the glyph. // Now we can render the glyph.
var bitmap: freetype.c.FT_Bitmap = undefined; var bitmap: freetype.c.FT_Bitmap = undefined;
_ = freetype.c.FT_Bitmap_Init(&bitmap); _ = freetype.c.FT_Bitmap_Init(&bitmap);
@ -641,42 +662,7 @@ pub const Face = struct {
// This should be the distance from the left of // This should be the distance from the left of
// the cell to the left of the glyph's bounding box. // the cell to the left of the glyph's bounding box.
const offset_x: i32 = offset_x: { const offset_x: i32 = @intFromFloat(@floor(x));
// If the glyph's advance is narrower than the cell width then we
// center the advance of the glyph within the cell width. At first
// I implemented this to proportionally scale the center position
// of the glyph but that messes up glyphs that are meant to align
// vertically with others, so this is a compromise.
//
// This makes it so that when the `adjust-cell-width` config is
// used, or when a fallback font with a different advance width
// is used, we don't get weirdly aligned glyphs.
//
// We don't do this if the constraint has a horizontal alignment,
// since in that case the position was already calculated with the
// new cell width in mind.
if (opts.constraint.align_horizontal == .none) {
const advance = f26dot6ToFloat(glyph.*.advance.x);
const new_advance =
cell_width * @as(f64, @floatFromInt(opts.cell_width orelse 1));
// If the original advance is greater than the cell width then
// it's possible that this is a ligature or other glyph that is
// intended to overflow the cell to one side or the other, and
// adjusting the bearings could mess that up, so we just leave
// it alone if that's the case.
//
// We also don't want to do anything if the advance is zero or
// less, since this is used for stuff like combining characters.
if (advance > new_advance or advance <= 0.0) {
break :offset_x @intFromFloat(@floor(x));
}
break :offset_x @intFromFloat(
@floor(x + (new_advance - advance) / 2),
);
} else {
break :offset_x @intFromFloat(@floor(x));
}
};
return Glyph{ return Glyph{
.width = px_width, .width = px_width,