From d4a75492225e571f91fcd6ef79662563fad9be94 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 8 May 2024 14:23:20 -0400 Subject: [PATCH] feat(font): Non-integer point sizes Allows for high dpi displays to get odd numbered pixel sizes, for example, 13.5pt @ 2px/pt for 27px font. This implementation performs all the sizing calculations with f32, rounding to the nearest pixel size when it comes to rendering. In the future this can be enhanced by adding fractional scaling to support fractional pixel sizes. --- src/Surface.zig | 14 +++++++------- src/cli/args.zig | 6 ++++-- src/config/Config.zig | 8 ++++++-- src/font/SharedGridSet.zig | 4 +++- src/font/discovery.zig | 8 ++++---- src/font/face.zig | 9 ++++----- src/input/Binding.zig | 4 ++-- 7 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 8266f3dbf..84d8cec2b 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -194,7 +194,7 @@ const DerivedConfig = struct { arena: ArenaAllocator, /// For docs for these, see the associated config they are derived from. - original_font_size: u8, + original_font_size: f32, keybind: configpkg.Keybinds, clipboard_read: configpkg.ClipboardAccess, clipboard_write: configpkg.ClipboardAccess, @@ -321,8 +321,8 @@ pub fn init( // The font size we desire along with the DPI determined for the surface const font_size: font.face.DesiredSize = .{ .points = config.@"font-size", - .xdpi = @intFromFloat(x_dpi), - .ydpi = @intFromFloat(y_dpi), + .xdpi = x_dpi, + .ydpi = y_dpi, }; // Setup our font group. This will reuse an existing font group if @@ -1703,8 +1703,8 @@ pub fn contentScaleCallback(self: *Surface, content_scale: apprt.ContentScale) ! // Update our font size which is dependent on the DPI const size = size: { var size = self.font_size; - size.xdpi = @intFromFloat(x_dpi); - size.ydpi = @intFromFloat(y_dpi); + size.xdpi = x_dpi; + size.ydpi = y_dpi; break :size size; }; @@ -3011,7 +3011,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool log.debug("increase font size={}", .{delta}); var size = self.font_size; - size.points +|= delta; + size.points = size.points + delta; try self.setFontSize(size); }, @@ -3019,7 +3019,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool log.debug("decrease font size={}", .{delta}); var size = self.font_size; - size.points = @max(1, size.points -| delta); + size.points = @max(1, size.points - delta); try self.setFontSize(size); }, diff --git a/src/cli/args.zig b/src/cli/args.zig index 49c5152ac..707416e38 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -250,8 +250,10 @@ fn parseIntoField( 0, ) catch return error.InvalidValue, - f64 => std.fmt.parseFloat( - f64, + f32, + f64, + => |Float| std.fmt.parseFloat( + Float, value orelse return error.ValueRequired, ) catch return error.InvalidValue, diff --git a/src/config/Config.zig b/src/config/Config.zig index a2d6f39a8..c565d538d 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -99,8 +99,12 @@ const c = @cImport({ /// separate repetitive entries in your config). @"font-feature": RepeatableString = .{}, -/// Font size in points -@"font-size": u8 = switch (builtin.os.tag) { +/// Font size in points. This value can be a non-integer and the nearest integer +/// pixel size will be selected. If you have a high dpi display where 1pt = 2px +/// then you can get an odd numbered pixel size by specifying a half point. +/// +/// For example, 13.5pt @ 2px/pt = 27px +@"font-size": f32 = switch (builtin.os.tag) { // On macOS we default a little bigger since this tends to look better. This // is purely subjective but this is easy to modify. .macos => 13, diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index daf39b54f..5114f18a1 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -575,7 +575,9 @@ pub const Key = struct { /// Hash the key with the given hasher. pub fn hash(self: Key, hasher: anytype) void { const autoHash = std.hash.autoHash; - autoHash(hasher, self.font_size); + autoHash(hasher, @as(u32, @bitCast(self.font_size.points))); + autoHash(hasher, @as(u32, @bitCast(self.font_size.xdpi))); + autoHash(hasher, @as(u32, @bitCast(self.font_size.ydpi))); autoHash(hasher, self.descriptors.len); for (self.descriptors) |d| d.hash(hasher); self.codepoint_map.hash(hasher); diff --git a/src/font/discovery.zig b/src/font/discovery.zig index a93574e07..eb2451768 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -49,7 +49,7 @@ pub const Descriptor = struct { /// Font size in points that the font should support. For conversion /// to pixels, we will use 72 DPI for Mac and 96 DPI for everything else. /// (If pixel conversion is necessary, i.e. emoji fonts) - size: u16 = 0, + size: f32 = 0, /// True if we want to search specifically for a font that supports /// specific styles. @@ -69,7 +69,7 @@ pub const Descriptor = struct { autoHashStrat(hasher, self.family, .Deep); autoHashStrat(hasher, self.style, .Deep); autoHash(hasher, self.codepoint); - autoHash(hasher, self.size); + autoHash(hasher, @as(u32, @bitCast(self.size))); autoHash(hasher, self.bold); autoHash(hasher, self.italic); autoHash(hasher, self.monospace); @@ -125,7 +125,7 @@ pub const Descriptor = struct { } if (self.size > 0) assert(pat.add( .size, - .{ .integer = self.size }, + .{ .integer = @round(self.size) }, false, )); if (self.bold) assert(pat.add( @@ -183,7 +183,7 @@ pub const Descriptor = struct { // Set our size attribute if set if (self.size > 0) { - const size32 = @as(i32, @intCast(self.size)); + const size32: i32 = @intFromFloat(@round(self.size)); const size = try macos.foundation.Number.create( .sint32, &size32, diff --git a/src/font/face.zig b/src/font/face.zig index 815629b44..0028d7396 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -35,17 +35,16 @@ pub const Options = struct { /// The desired size for loading a font. pub const DesiredSize = struct { // Desired size in points - points: u8, + points: f32, // The DPI of the screen so we can convert points to pixels. - xdpi: u16 = default_dpi, - ydpi: u16 = default_dpi, + xdpi: f32 = default_dpi, + ydpi: f32 = default_dpi, // Converts points to pixels pub fn pixels(self: DesiredSize) u16 { // 1 point = 1/72 inch - const points_u16: u16 = @intCast(self.points); - return (points_u16 * self.ydpi) / 72; + return @intFromFloat(@round((self.points * self.ydpi) / 72)); } }; diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 4a153035d..200fd6d58 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -158,8 +158,8 @@ pub const Action = union(enum) { paste_from_selection: void, /// Increase/decrease the font size by a certain amount. - increase_font_size: u8, - decrease_font_size: u8, + increase_font_size: f32, + decrease_font_size: f32, /// Reset the font size to the original configured size. reset_font_size: void,