mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
Merge pull request #178 from mitchellh/font-integer
Change font metrics to all be integers, not floats.
This commit is contained in:
@ -429,8 +429,8 @@ pub fn init(
|
||||
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
|
||||
// but is otherwise somewhat arbitrary.
|
||||
try rt_surface.setSizeLimits(.{
|
||||
.width = @intFromFloat(cell_size.width * 10),
|
||||
.height = @intFromFloat(cell_size.height * 4),
|
||||
.width = cell_size.width * 10,
|
||||
.height = cell_size.height * 4,
|
||||
}, null);
|
||||
|
||||
// Call our size callback which handles all our retina setup
|
||||
@ -662,10 +662,10 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
||||
|
||||
const x: f64 = x: {
|
||||
// Simple x * cell width gives the top-left corner
|
||||
var x: f64 = @floatCast(@as(f32, @floatFromInt(cursor.x)) * self.cell_size.width);
|
||||
var x: f64 = @floatFromInt(cursor.x * self.cell_size.width);
|
||||
|
||||
// We want the midpoint
|
||||
x += self.cell_size.width / 2;
|
||||
x += @as(f64, @floatFromInt(self.cell_size.width)) / 2;
|
||||
|
||||
// And scale it
|
||||
x /= content_scale.x;
|
||||
@ -675,10 +675,10 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
||||
|
||||
const y: f64 = y: {
|
||||
// Simple x * cell width gives the top-left corner
|
||||
var y: f64 = @floatCast(@as(f32, @floatFromInt(cursor.y)) * self.cell_size.height);
|
||||
var y: f64 = @floatFromInt(cursor.y * self.cell_size.height);
|
||||
|
||||
// We want the bottom
|
||||
y += self.cell_size.height;
|
||||
y += @floatFromInt(self.cell_size.height);
|
||||
|
||||
// And scale it
|
||||
y /= content_scale.y;
|
||||
@ -1332,7 +1332,7 @@ pub fn scrollCallback(
|
||||
|
||||
// If the new offset is less than a single unit of scroll, we save
|
||||
// the new pending value and do not scroll yet.
|
||||
const cell_size = self.cell_size.height;
|
||||
const cell_size: f64 = @floatFromInt(self.cell_size.height);
|
||||
if (@fabs(poff) < cell_size) {
|
||||
self.mouse.pending_scroll_y = poff;
|
||||
break :y .{};
|
||||
@ -1359,7 +1359,7 @@ pub fn scrollCallback(
|
||||
}
|
||||
|
||||
const poff = self.mouse.pending_scroll_x + (xoff * -1);
|
||||
const cell_size = self.cell_size.width;
|
||||
const cell_size: f64 = @floatFromInt(self.cell_size.width);
|
||||
if (@fabs(poff) < cell_size) {
|
||||
self.mouse.pending_scroll_x = poff;
|
||||
break :x .{};
|
||||
@ -1724,7 +1724,7 @@ pub fn mouseButtonCallback(
|
||||
// If we move our cursor too much between clicks then we reset
|
||||
// the multi-click state.
|
||||
if (self.mouse.left_click_count > 0) {
|
||||
const max_distance = self.cell_size.width;
|
||||
const max_distance: f64 = @floatFromInt(self.cell_size.width);
|
||||
const distance = @sqrt(
|
||||
std.math.pow(f64, pos.x - self.mouse.left_click_xpos, 2) +
|
||||
std.math.pow(f64, pos.y - self.mouse.left_click_ypos, 2),
|
||||
@ -1951,10 +1951,13 @@ fn dragLeftClickSingle(
|
||||
//
|
||||
|
||||
// the boundary point at which we consider selection or non-selection
|
||||
const cell_xboundary = self.cell_size.width * 0.6;
|
||||
const cell_xboundary = @as(f32, @floatFromInt(self.cell_size.width)) * 0.6;
|
||||
|
||||
// first xpos of the clicked cell
|
||||
const cell_xstart = @as(f32, @floatFromInt(self.mouse.left_click_point.x)) * self.cell_size.width;
|
||||
const cell_xstart = @as(
|
||||
f32,
|
||||
@floatFromInt(self.mouse.left_click_point.x),
|
||||
) * @as(f32, @floatFromInt(self.cell_size.width));
|
||||
const cell_start_xpos = self.mouse.left_click_xpos - cell_xstart;
|
||||
|
||||
// If this is the same cell, then we only start the selection if weve
|
||||
@ -2038,7 +2041,7 @@ fn posToViewport(self: Surface, xpos: f64, ypos: f64) terminal.point.Viewport {
|
||||
return .{
|
||||
.x = if (xpos_adjusted < 0) 0 else x: {
|
||||
// Our cell is the mouse divided by cell width
|
||||
const cell_width: f64 = @floatCast(self.cell_size.width);
|
||||
const cell_width: f64 = @floatFromInt(self.cell_size.width);
|
||||
const x: usize = @intFromFloat(xpos_adjusted / cell_width);
|
||||
|
||||
// Can be off the screen if the user drags it out, so max
|
||||
@ -2047,7 +2050,7 @@ fn posToViewport(self: Surface, xpos: f64, ypos: f64) terminal.point.Viewport {
|
||||
},
|
||||
|
||||
.y = if (ypos_adjusted < 0) 0 else y: {
|
||||
const cell_height: f64 = @floatCast(self.cell_size.height);
|
||||
const cell_height: f64 = @floatFromInt(self.cell_size.height);
|
||||
const y: usize = @intFromFloat(ypos_adjusted / cell_height);
|
||||
break :y @min(y, self.grid_size.rows - 1);
|
||||
},
|
||||
|
@ -329,10 +329,10 @@ pub const Wasm = struct {
|
||||
|
||||
// Set details for our sprite font
|
||||
self.sprite = font.sprite.Face{
|
||||
.width = @intFromFloat(metrics.cell_width),
|
||||
.height = @intFromFloat(metrics.cell_height),
|
||||
.width = metrics.cell_width,
|
||||
.height = metrics.cell_height,
|
||||
.thickness = 2,
|
||||
.underline_position = @intFromFloat(metrics.underline_position),
|
||||
.underline_position = metrics.underline_position,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -39,23 +39,23 @@ pub const DesiredSize = struct {
|
||||
/// Metrics associated with the font that are useful for renderers to know.
|
||||
pub const Metrics = struct {
|
||||
/// Recommended cell width and height for a monospace grid using this font.
|
||||
cell_width: f32,
|
||||
cell_height: f32,
|
||||
cell_width: u32,
|
||||
cell_height: u32,
|
||||
|
||||
/// For monospace grids, the recommended y-value from the bottom to set
|
||||
/// the baseline for font rendering. This is chosen so that things such
|
||||
/// as the bottom of a "g" or "y" do not drop below the cell.
|
||||
cell_baseline: f32,
|
||||
cell_baseline: u32,
|
||||
|
||||
/// The position of the underline from the top of the cell and the
|
||||
/// thickness in pixels.
|
||||
underline_position: f32,
|
||||
underline_thickness: f32,
|
||||
underline_position: u32,
|
||||
underline_thickness: u32,
|
||||
|
||||
/// The position and thickness of a strikethrough. Same units/style
|
||||
/// as the underline fields.
|
||||
strikethrough_position: f32,
|
||||
strikethrough_thickness: f32,
|
||||
strikethrough_position: u32,
|
||||
strikethrough_thickness: u32,
|
||||
};
|
||||
|
||||
/// Additional options for rendering glyphs.
|
||||
|
@ -261,7 +261,7 @@ pub const Face = struct {
|
||||
const offset_y: i32 = offset_y: {
|
||||
// Our Y coordinate in 3D is (0, 0) bottom left, +y is UP.
|
||||
// We need to calculate our baseline from the bottom of a cell.
|
||||
const baseline_from_bottom = self.metrics.cell_height - self.metrics.cell_baseline;
|
||||
const baseline_from_bottom: f64 = @floatFromInt(self.metrics.cell_height - self.metrics.cell_baseline);
|
||||
|
||||
// Next we offset our baseline by the bearing in the font. We
|
||||
// ADD here because CoreText y is UP.
|
||||
@ -328,7 +328,7 @@ pub const Face = struct {
|
||||
max = @max(advances[i].width, max);
|
||||
}
|
||||
|
||||
break :cell_width @floatCast(max);
|
||||
break :cell_width @floatCast(@ceil(max));
|
||||
};
|
||||
|
||||
// Calculate the cell height by using CoreText's layout engine
|
||||
@ -424,8 +424,8 @@ pub const Face = struct {
|
||||
};
|
||||
|
||||
// All of these metrics are based on our layout above.
|
||||
const cell_height = layout_metrics.height;
|
||||
const cell_baseline = layout_metrics.ascent;
|
||||
const cell_height = @ceil(layout_metrics.height);
|
||||
const cell_baseline = @ceil(layout_metrics.ascent);
|
||||
const underline_thickness = @ceil(@as(f32, @floatCast(ct_font.getUnderlineThickness())));
|
||||
const strikethrough_position = cell_baseline * 0.6;
|
||||
const strikethrough_thickness = underline_thickness;
|
||||
@ -441,24 +441,20 @@ pub const Face = struct {
|
||||
// const units_per_em = ct_font.getUnitsPerEm();
|
||||
// const units_per_point = @intToFloat(f64, units_per_em) / ct_font.getSize();
|
||||
|
||||
// std.log.warn("font size size={d}", .{ct_font.getSize()});
|
||||
// std.log.warn("font metrics width={d}, height={d} baseline={d} underline_pos={d} underline_thickness={d}", .{
|
||||
// cell_width,
|
||||
// cell_height,
|
||||
// cell_baseline,
|
||||
// underline_position,
|
||||
// underline_thickness,
|
||||
// });
|
||||
|
||||
return font.face.Metrics{
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.cell_baseline = cell_baseline,
|
||||
.underline_position = underline_position,
|
||||
.underline_thickness = underline_thickness,
|
||||
.strikethrough_position = strikethrough_position,
|
||||
.strikethrough_thickness = strikethrough_thickness,
|
||||
const result = font.face.Metrics{
|
||||
.cell_width = @intFromFloat(cell_width),
|
||||
.cell_height = @intFromFloat(cell_height),
|
||||
.cell_baseline = @intFromFloat(cell_baseline),
|
||||
.underline_position = @intFromFloat(underline_position),
|
||||
.underline_thickness = @intFromFloat(underline_thickness),
|
||||
.strikethrough_position = @intFromFloat(strikethrough_position),
|
||||
.strikethrough_thickness = @intFromFloat(strikethrough_thickness),
|
||||
};
|
||||
|
||||
// std.log.warn("font size size={d}", .{ct_font.getSize()});
|
||||
// std.log.warn("font metrics={}", .{result});
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -325,7 +325,7 @@ pub const Face = struct {
|
||||
// baseline calculation. The baseline calculation is so that everything
|
||||
// is properly centered when we render it out into a monospace grid.
|
||||
// Note: we add here because our X/Y is actually reversed, adding goes UP.
|
||||
break :offset_y glyph_metrics.bitmap_top + @as(c_int, @intFromFloat(self.metrics.cell_baseline));
|
||||
break :offset_y glyph_metrics.bitmap_top + @as(c_int, @intCast(self.metrics.cell_baseline));
|
||||
};
|
||||
|
||||
// log.warn("renderGlyph width={} height={} offset_x={} offset_y={} glyph_metrics={}", .{
|
||||
@ -475,25 +475,20 @@ pub const Face = struct {
|
||||
.thickness = underline_thickness,
|
||||
};
|
||||
|
||||
// log.warn("METRICS={} width={d} height={d} baseline={d} underline_pos={d} underline_thickness={d} strikethrough={}", .{
|
||||
// size_metrics,
|
||||
// cell_width,
|
||||
// cell_height,
|
||||
// cell_height - cell_baseline,
|
||||
// underline_position,
|
||||
// underline_thickness,
|
||||
// strikethrough,
|
||||
// });
|
||||
|
||||
return .{
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.cell_baseline = cell_baseline,
|
||||
.underline_position = underline_position,
|
||||
.underline_thickness = underline_thickness,
|
||||
.strikethrough_position = strikethrough.pos,
|
||||
.strikethrough_thickness = strikethrough.thickness,
|
||||
const result = font.face.Metrics{
|
||||
.cell_width = @intFromFloat(cell_width),
|
||||
.cell_height = @intFromFloat(cell_height),
|
||||
.cell_baseline = @intFromFloat(cell_baseline),
|
||||
.underline_position = @intFromFloat(underline_position),
|
||||
.underline_thickness = @intFromFloat(underline_thickness),
|
||||
.strikethrough_position = @intFromFloat(strikethrough.pos),
|
||||
.strikethrough_thickness = @intFromFloat(strikethrough.thickness),
|
||||
};
|
||||
|
||||
// std.log.warn("font size size={d}", .{ct_font.getSize()});
|
||||
// std.log.warn("font metrics={}", .{result});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert freetype "font units" to pixels using the Y scale.
|
||||
|
@ -273,16 +273,17 @@ pub const Face = struct {
|
||||
const underline_position = cell_height - 1;
|
||||
const underline_thickness: f32 = 1;
|
||||
|
||||
self.metrics = .{
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.cell_baseline = cell_baseline,
|
||||
.underline_position = underline_position,
|
||||
.underline_thickness = underline_thickness,
|
||||
.strikethrough_position = underline_position,
|
||||
.strikethrough_thickness = underline_thickness,
|
||||
const result = font.face.Metrics{
|
||||
.cell_width = @intFromFloat(cell_width),
|
||||
.cell_height = @intFromFloat(cell_height),
|
||||
.cell_baseline = @intFromFloat(cell_baseline),
|
||||
.underline_position = @intFromFloat(underline_position),
|
||||
.underline_thickness = @intFromFloat(underline_thickness),
|
||||
.strikethrough_position = @intFromFloat(underline_position),
|
||||
.strikethrough_thickness = @intFromFloat(underline_thickness),
|
||||
};
|
||||
|
||||
self.metrics = result;
|
||||
log.debug("metrics font={s} value={}", .{ self.font_str, self.metrics });
|
||||
}
|
||||
|
||||
|
@ -216,10 +216,10 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
|
||||
// Set the sprite font up
|
||||
options.font_group.group.sprite = font.sprite.Face{
|
||||
.width = @intFromFloat(metrics.cell_width),
|
||||
.height = @intFromFloat(metrics.cell_height),
|
||||
.width = metrics.cell_width,
|
||||
.height = metrics.cell_height,
|
||||
.thickness = 2,
|
||||
.underline_position = @intFromFloat(metrics.underline_position),
|
||||
.underline_position = metrics.underline_position,
|
||||
};
|
||||
|
||||
// Create the font shaper. We initially create a shaper that can support
|
||||
@ -301,8 +301,8 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
.uniforms = .{
|
||||
.projection_matrix = undefined,
|
||||
.cell_size = undefined,
|
||||
.strikethrough_position = metrics.strikethrough_position,
|
||||
.strikethrough_thickness = metrics.strikethrough_thickness,
|
||||
.strikethrough_position = @floatFromInt(metrics.strikethrough_position),
|
||||
.strikethrough_thickness = @floatFromInt(metrics.strikethrough_thickness),
|
||||
},
|
||||
|
||||
// Fonts
|
||||
@ -460,9 +460,12 @@ pub fn setFontSize(self: *Metal, size: font.face.DesiredSize) !void {
|
||||
// Update our uniforms
|
||||
self.uniforms = .{
|
||||
.projection_matrix = self.uniforms.projection_matrix,
|
||||
.cell_size = .{ new_cell_size.width, new_cell_size.height },
|
||||
.strikethrough_position = metrics.strikethrough_position,
|
||||
.strikethrough_thickness = metrics.strikethrough_thickness,
|
||||
.cell_size = .{
|
||||
@floatFromInt(new_cell_size.width),
|
||||
@floatFromInt(new_cell_size.height),
|
||||
},
|
||||
.strikethrough_position = @floatFromInt(metrics.strikethrough_position),
|
||||
.strikethrough_thickness = @floatFromInt(metrics.strikethrough_thickness),
|
||||
};
|
||||
|
||||
// Recalculate our cell size. If it is the same as before, then we do
|
||||
@ -480,10 +483,10 @@ pub fn setFontSize(self: *Metal, size: font.face.DesiredSize) !void {
|
||||
|
||||
// Set the sprite font up
|
||||
self.font_group.group.sprite = font.sprite.Face{
|
||||
.width = @intFromFloat(self.cell_size.width),
|
||||
.height = @intFromFloat(self.cell_size.height),
|
||||
.width = self.cell_size.width,
|
||||
.height = self.cell_size.height,
|
||||
.thickness = 2,
|
||||
.underline_position = @intFromFloat(metrics.underline_position),
|
||||
.underline_position = metrics.underline_position,
|
||||
};
|
||||
|
||||
// Notify the window that the cell size changed.
|
||||
@ -792,7 +795,10 @@ pub fn setScreenSize(self: *Metal, dim: renderer.ScreenSize) !void {
|
||||
@as(f32, @floatFromInt(padded_dim.height)) + padding.bottom,
|
||||
-1 * padding.top,
|
||||
),
|
||||
.cell_size = .{ self.cell_size.width, self.cell_size.height },
|
||||
.cell_size = .{
|
||||
@floatFromInt(self.cell_size.width),
|
||||
@floatFromInt(self.cell_size.height),
|
||||
},
|
||||
.strikethrough_position = old.strikethrough_position,
|
||||
.strikethrough_thickness = old.strikethrough_thickness,
|
||||
};
|
||||
@ -1008,7 +1014,7 @@ pub fn updateCell(
|
||||
shaper_run.font_index,
|
||||
shaper_cell.glyph_index,
|
||||
.{
|
||||
.max_height = @intFromFloat(@ceil(self.cell_size.height)),
|
||||
.max_height = @intCast(self.cell_size.height),
|
||||
.thicken = self.config.font_thicken,
|
||||
},
|
||||
);
|
||||
|
@ -161,10 +161,19 @@ const SetFontSize = struct {
|
||||
fn apply(self: SetFontSize, r: *const OpenGL) !void {
|
||||
try r.program.setUniform(
|
||||
"cell_size",
|
||||
@Vector(2, f32){ self.metrics.cell_width, self.metrics.cell_height },
|
||||
@Vector(2, f32){
|
||||
@floatFromInt(self.metrics.cell_width),
|
||||
@floatFromInt(self.metrics.cell_height),
|
||||
},
|
||||
);
|
||||
try r.program.setUniform(
|
||||
"strikethrough_position",
|
||||
@as(f32, @floatFromInt(self.metrics.strikethrough_position)),
|
||||
);
|
||||
try r.program.setUniform(
|
||||
"strikethrough_thickness",
|
||||
@as(f32, @floatFromInt(self.metrics.strikethrough_thickness)),
|
||||
);
|
||||
try r.program.setUniform("strikethrough_position", self.metrics.strikethrough_position);
|
||||
try r.program.setUniform("strikethrough_thickness", self.metrics.strikethrough_thickness);
|
||||
}
|
||||
};
|
||||
|
||||
@ -661,10 +670,10 @@ fn resetFontMetrics(
|
||||
|
||||
// Set details for our sprite font
|
||||
font_group.group.sprite = font.sprite.Face{
|
||||
.width = @intFromFloat(metrics.cell_width),
|
||||
.height = @intFromFloat(metrics.cell_height),
|
||||
.width = metrics.cell_width,
|
||||
.height = metrics.cell_height,
|
||||
.thickness = 2,
|
||||
.underline_position = @intFromFloat(metrics.underline_position),
|
||||
.underline_position = metrics.underline_position,
|
||||
};
|
||||
|
||||
return metrics;
|
||||
@ -1148,7 +1157,7 @@ pub fn updateCell(
|
||||
shaper_run.font_index,
|
||||
shaper_cell.glyph_index,
|
||||
.{
|
||||
.max_height = @intFromFloat(@ceil(self.cell_size.height)),
|
||||
.max_height = @intCast(self.cell_size.height),
|
||||
.thicken = self.config.font_thicken,
|
||||
},
|
||||
);
|
||||
|
@ -101,7 +101,10 @@ pub inline fn setUniform(
|
||||
c.GL_FALSE,
|
||||
@ptrCast(&value),
|
||||
),
|
||||
else => unreachable,
|
||||
else => {
|
||||
log.warn("unsupported uniform type {}", .{@TypeOf(value)});
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
try errors.getError();
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ const log = std.log.scoped(.renderer_size);
|
||||
/// The units for the width and height are in world space. They have to
|
||||
/// be normalized for any renderer implementation.
|
||||
pub const CellSize = struct {
|
||||
width: f32,
|
||||
height: f32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
/// Initialize the cell size information from a font group. This ensures
|
||||
/// that all renderers use the same cell sizing information for the same
|
||||
@ -71,8 +71,14 @@ pub const GridSize = struct {
|
||||
/// Update the columns/rows for the grid based on the given screen and
|
||||
/// cell size.
|
||||
pub fn update(self: *GridSize, screen: ScreenSize, cell: CellSize) void {
|
||||
self.columns = @max(1, @as(Unit, @intFromFloat(@as(f32, @floatFromInt(screen.width)) / cell.width)));
|
||||
self.rows = @max(1, @as(Unit, @intFromFloat(@as(f32, @floatFromInt(screen.height)) / cell.height)));
|
||||
const cell_width: f32 = @floatFromInt(cell.width);
|
||||
const cell_height: f32 = @floatFromInt(cell.height);
|
||||
const screen_width: f32 = @floatFromInt(screen.width);
|
||||
const screen_height: f32 = @floatFromInt(screen.height);
|
||||
const calc_cols: Unit = @intFromFloat(screen_width / cell_width);
|
||||
const calc_rows: Unit = @intFromFloat(screen_height / cell_height);
|
||||
self.columns = @max(1, calc_cols);
|
||||
self.rows = @max(1, calc_rows);
|
||||
}
|
||||
};
|
||||
|
||||
@ -86,9 +92,13 @@ pub const Padding = struct {
|
||||
/// Returns padding that balances the whitespace around the screen
|
||||
/// for the given grid and cell sizes.
|
||||
pub fn balanced(screen: ScreenSize, grid: GridSize, cell: CellSize) Padding {
|
||||
// Turn our cell sizes into floats for the math
|
||||
const cell_width: f32 = @floatFromInt(cell.width);
|
||||
const cell_height: f32 = @floatFromInt(cell.height);
|
||||
|
||||
// The size of our full grid
|
||||
const grid_width = @as(f32, @floatFromInt(grid.columns)) * cell.width;
|
||||
const grid_height = @as(f32, @floatFromInt(grid.rows)) * cell.height;
|
||||
const grid_width = @as(f32, @floatFromInt(grid.columns)) * cell_width;
|
||||
const grid_height = @as(f32, @floatFromInt(grid.rows)) * cell_height;
|
||||
|
||||
// The empty space to the right of a line and bottom of the last row
|
||||
const space_right = @as(f32, @floatFromInt(screen.width)) - grid_width;
|
||||
|
Reference in New Issue
Block a user