From 3d4aacd51dddef84674bd12437c9c4c243f29f17 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 4 Oct 2022 11:08:07 -0700 Subject: [PATCH] underline is derived from the primary font --- TODO.md | 1 - shaders/cell.v.glsl | 15 +++++++-------- src/Grid.zig | 3 ++- src/font/Face.zig | 36 ++++++++++++++++++++++++++++++++---- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/TODO.md b/TODO.md index 608e1b1f9..436ff324e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ Bugs: -* Underline should use freetype underline thickness hint * Glyph baseline is using the main font, but it can vary font to font Performance: diff --git a/shaders/cell.v.glsl b/shaders/cell.v.glsl index 873ce81ce..a282981f5 100644 --- a/shaders/cell.v.glsl +++ b/shaders/cell.v.glsl @@ -57,7 +57,8 @@ uniform sampler2D text; uniform sampler2D text_color; uniform vec2 cell_size; uniform mat4 projection; -uniform float glyph_baseline; +uniform float underline_position; +uniform float underline_thickness; /******************************************************************** * Modes @@ -196,18 +197,16 @@ void main() { break; case MODE_UNDERLINE: - // Make the underline a smaller version of our cell - // TODO: use real font underline thickness - vec2 underline_size = vec2(cell_size_scaled.x, cell_size_scaled.y*0.05); + // Underline Y value is just our thickness + vec2 underline_size = vec2(cell_size_scaled.x, underline_thickness); - // Position our underline so that it is midway between the glyph - // baseline and the bottom of the cell. - vec2 underline_offset = vec2(cell_size_scaled.x, cell_size_scaled.y - (glyph_baseline / 2)); + // Position the underline where we are told to + vec2 underline_offset = vec2(cell_size_scaled.x, underline_position) ; // Go to the bottom of the cell, take away the size of the // underline, and that is our position. We also float it slightly // above the bottom. - cell_pos = cell_pos + underline_offset - underline_size * position; + cell_pos = cell_pos + underline_offset - (underline_size * position); gl_Position = projection * vec4(cell_pos, cell_z, 1.0); color = fg_color_in / 255.0; diff --git a/src/Grid.zig b/src/Grid.zig index 73fe9092b..ee5efa7d2 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -178,7 +178,8 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid { const pbind = try program.use(); defer pbind.unbind(); try program.setUniform("cell_size", @Vector(2, f32){ metrics.cell_width, metrics.cell_height }); - try program.setUniform("glyph_baseline", metrics.cell_baseline); + try program.setUniform("underline_position", metrics.underline_position); + try program.setUniform("underline_thickness", metrics.underline_thickness); // Set all of our texture indexes try program.setUniform("text", 0); diff --git a/src/font/Face.zig b/src/font/Face.zig index 765a772b6..ef523c30d 100644 --- a/src/font/Face.zig +++ b/src/font/Face.zig @@ -229,7 +229,7 @@ pub fn renderGlyph(self: Face, alloc: Allocator, atlas: *Atlas, glyph_index: u32 /// Convert 16.6 pixel format to pixels based on the scale factor of the /// current font size. -pub fn unitsToPxY(self: Face, units: i32) i32 { +fn unitsToPxY(self: Face, units: i32) i32 { return @intCast(i32, freetype.mulFix( units, @intCast(i32, self.face.handle.*.size.*.metrics.y_scale), @@ -251,6 +251,11 @@ pub const Metrics = struct { /// 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, + + /// The position of the underline from the top of the cell and the + /// thickness in pixels. + underline_position: f32, + underline_thickness: f32, }; /// Calculate the metrics associated with a face. This is not public because @@ -319,18 +324,41 @@ fn calcMetrics(face: freetype.Face) Metrics { // is reversed. const cell_baseline = -1 * f26dot6ToFloat(size_metrics.descender); - // log.warn("METRICS={} width={} height={} baseline={} baseline_desc={}", .{ + // The underline position. This is a value from the top where the + // underline should go. + const underline_position = underline_pos: { + // We use the declared underline position if its available + const declared = freetype.mulFix( + @intCast(i32, size_metrics.descender) + face.handle.*.underline_position, + @intCast(i32, size_metrics.x_scale), + ) >> 6; + if (declared > 0) + break :underline_pos @intToFloat(f32, declared); + + // If we have no declared underline position, we go slightly under the + // cell height (mainly: non-scalable fonts, i.e. emoji) + break :underline_pos cell_height - 1; + }; + const underline_thickness = @intToFloat(f32, @maximum(1, freetype.mulFix( + face.handle.*.underline_thickness, + @intCast(i32, size_metrics.x_scale), + ) >> 6)); + + // log.warn("METRICS={} width={} height={} baseline={} underline_pos={} underline_thickness={}", .{ // size_metrics, // cell_width, // cell_height, - // f26dot6ToFloat(size_metrics.ascender), - // -1 * f26dot6ToFloat(size_metrics.descender), + // cell_baseline, + // cell_height - underline_position, + // underline_thickness, // }); return .{ .cell_width = cell_width, .cell_height = cell_height, .cell_baseline = cell_baseline, + .underline_position = underline_position, + .underline_thickness = underline_thickness, }; }