font/coretext: fix horizontal bearing calculation (#7848)

This was subtly wrong in a way that was most obvious when text switched
from regular to bold, where it would seem to wiggle since the bearings
of each letter would shift by a pixel in either direction. This affected
applications like fzf which uses bold to dynamically highlight the line
you have selected.
This commit is contained in:
Mitchell Hashimoto
2025-07-07 08:59:31 -07:00
committed by GitHub

View File

@ -366,8 +366,10 @@ pub const Face = struct {
// of extra width to the area that's drawn in beyond just the width of // of extra width to the area that's drawn in beyond just the width of
// the glyph itself, so we include that extra fraction of a pixel when // the glyph itself, so we include that extra fraction of a pixel when
// calculating the width and height here. // calculating the width and height here.
const px_width: u32 = @intFromFloat(@ceil(width + rect.origin.x - @floor(rect.origin.x))); const frac_x = rect.origin.x - @floor(rect.origin.x);
const px_height: u32 = @intFromFloat(@ceil(height + rect.origin.y - @floor(rect.origin.y))); const frac_y = rect.origin.y - @floor(rect.origin.y);
const px_width: u32 = @intFromFloat(@ceil(width + frac_x));
const px_height: u32 = @intFromFloat(@ceil(height + frac_y));
// Settings that are specific to if we are rendering text or emoji. // Settings that are specific to if we are rendering text or emoji.
const color: struct { const color: struct {
@ -513,13 +515,13 @@ pub const Face = struct {
// We also don't want to do anything if the advance is zero or // We also don't want to do anything if the advance is zero or
// less, since this is used for stuff like combining characters. // less, since this is used for stuff like combining characters.
if (advance > new_advance or advance <= 0.0) { if (advance > new_advance or advance <= 0.0) {
break :offset_x @intFromFloat(@round(x)); break :offset_x @intFromFloat(@ceil(x - frac_x));
} }
break :offset_x @intFromFloat( break :offset_x @intFromFloat(
@round(x + (new_advance - advance) / 2), @ceil(x - frac_x + (new_advance - advance) / 2),
); );
} else { } else {
break :offset_x @intFromFloat(@round(x)); break :offset_x @intFromFloat(@ceil(x - frac_x));
} }
}; };