mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #2148 from ghostty-org/ft-bold
font/freetype: synthetic bold
This commit is contained in:
@ -95,6 +95,14 @@ pub const Face = struct {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a given glyph image to a bitmap.
|
||||||
|
pub fn renderGlyph(self: Face, render_mode: RenderMode) Error!void {
|
||||||
|
return intToError(c.FT_Render_Glyph(
|
||||||
|
self.handle.*.glyph,
|
||||||
|
@intFromEnum(render_mode),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a pointer to a given SFNT table stored within a face.
|
/// Return a pointer to a given SFNT table stored within a face.
|
||||||
pub fn getSfntTable(self: Face, comptime tag: SfntTag) ?*tag.DataType() {
|
pub fn getSfntTable(self: Face, comptime tag: SfntTag) ?*tag.DataType() {
|
||||||
return @ptrCast(@alignCast(c.FT_Get_Sfnt_Table(
|
return @ptrCast(@alignCast(c.FT_Get_Sfnt_Table(
|
||||||
@ -231,6 +239,16 @@ pub const Encoding = enum(u31) {
|
|||||||
apple_roman = c.FT_ENCODING_APPLE_ROMAN,
|
apple_roman = c.FT_ENCODING_APPLE_ROMAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_render_mode
|
||||||
|
pub const RenderMode = enum(c_uint) {
|
||||||
|
normal = c.FT_RENDER_MODE_NORMAL,
|
||||||
|
light = c.FT_RENDER_MODE_LIGHT,
|
||||||
|
mono = c.FT_RENDER_MODE_MONO,
|
||||||
|
lcd = c.FT_RENDER_MODE_LCD,
|
||||||
|
lcd_v = c.FT_RENDER_MODE_LCD_V,
|
||||||
|
sdf = c.FT_RENDER_MODE_SDF,
|
||||||
|
};
|
||||||
|
|
||||||
/// A list of bit field constants for FT_Load_Glyph to indicate what kind of
|
/// A list of bit field constants for FT_Load_Glyph to indicate what kind of
|
||||||
/// operations to perform during glyph loading.
|
/// operations to perform during glyph loading.
|
||||||
pub const LoadFlags = packed struct {
|
pub const LoadFlags = packed struct {
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
#include FT_TRUETYPE_TABLES_H
|
#include FT_TRUETYPE_TABLES_H
|
||||||
#include <freetype/ftmm.h>
|
#include <freetype/ftmm.h>
|
||||||
|
#include <freetype/ftoutln.h>
|
||||||
#include <freetype/ftsnames.h>
|
#include <freetype/ftsnames.h>
|
||||||
#include <freetype/ttnameid.h>
|
#include <freetype/ttnameid.h>
|
||||||
|
@ -38,7 +38,10 @@ pub const Face = struct {
|
|||||||
quirks_disable_default_font_features: bool = false,
|
quirks_disable_default_font_features: bool = false,
|
||||||
|
|
||||||
/// Set to true to apply a synthetic italic to the face.
|
/// Set to true to apply a synthetic italic to the face.
|
||||||
synthetic_italic: bool = false,
|
synthetic: packed struct {
|
||||||
|
italic: bool = false,
|
||||||
|
bold: bool = false,
|
||||||
|
} = .{},
|
||||||
|
|
||||||
/// The matrix applied to a regular font to create a synthetic italic.
|
/// The matrix applied to a regular font to create a synthetic italic.
|
||||||
const italic_matrix: freetype.c.FT_Matrix = .{
|
const italic_matrix: freetype.c.FT_Matrix = .{
|
||||||
@ -130,6 +133,25 @@ pub const Face = struct {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a new face that is the same as this but also has synthetic
|
||||||
|
/// bold applied.
|
||||||
|
pub fn syntheticBold(self: *const Face, opts: font.face.Options) !Face {
|
||||||
|
// Increase face ref count
|
||||||
|
self.face.ref();
|
||||||
|
errdefer self.face.deinit();
|
||||||
|
|
||||||
|
var f = try initFace(
|
||||||
|
.{ .lib = self.lib },
|
||||||
|
self.face,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
errdefer f.deinit();
|
||||||
|
f.synthetic = self.synthetic;
|
||||||
|
f.synthetic.bold = true;
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a new face that is the same as this but has a transformation
|
/// Return a new face that is the same as this but has a transformation
|
||||||
/// matrix applied to italicize it.
|
/// matrix applied to italicize it.
|
||||||
pub fn syntheticItalic(self: *const Face, opts: font.face.Options) !Face {
|
pub fn syntheticItalic(self: *const Face, opts: font.face.Options) !Face {
|
||||||
@ -143,7 +165,8 @@ pub const Face = struct {
|
|||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
errdefer f.deinit();
|
errdefer f.deinit();
|
||||||
f.synthetic_italic = true;
|
f.synthetic = self.synthetic;
|
||||||
|
f.synthetic.italic = true;
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@ -278,14 +301,17 @@ pub const Face = struct {
|
|||||||
// If we have synthetic italic, then we apply a transformation matrix.
|
// If we have synthetic italic, then we apply a transformation matrix.
|
||||||
// We have to undo this because synthetic italic works by increasing
|
// We have to undo this because synthetic italic works by increasing
|
||||||
// the ref count of the base face.
|
// the ref count of the base face.
|
||||||
if (self.synthetic_italic) self.face.setTransform(&italic_matrix, null);
|
if (self.synthetic.italic) self.face.setTransform(&italic_matrix, null);
|
||||||
defer if (self.synthetic_italic) self.face.setTransform(null, null);
|
defer if (self.synthetic.italic) self.face.setTransform(null, null);
|
||||||
|
|
||||||
// If our glyph has color, we want to render the color
|
// If our glyph has color, we want to render the color
|
||||||
try self.face.loadGlyph(glyph_index, .{
|
try self.face.loadGlyph(glyph_index, .{
|
||||||
.render = true,
|
|
||||||
.color = self.face.hasColor(),
|
.color = self.face.hasColor(),
|
||||||
|
|
||||||
|
// If we have synthetic bold, we have to set some additional
|
||||||
|
// glyph properties before render so we don't render here.
|
||||||
|
.render = !self.synthetic.bold,
|
||||||
|
|
||||||
// Disable bitmap strikes for now since it causes issues with
|
// Disable bitmap strikes for now since it causes issues with
|
||||||
// our cell metrics and rasterization. In the future, this is
|
// our cell metrics and rasterization. In the future, this is
|
||||||
// all fixable so we can enable it.
|
// all fixable so we can enable it.
|
||||||
@ -294,12 +320,23 @@ pub const Face = struct {
|
|||||||
// often colored bitmaps, which we support.
|
// often colored bitmaps, which we support.
|
||||||
.no_bitmap = !self.face.hasColor(),
|
.no_bitmap = !self.face.hasColor(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const glyph = self.face.handle.*.glyph;
|
const glyph = self.face.handle.*.glyph;
|
||||||
const bitmap_ft = glyph.*.bitmap;
|
|
||||||
|
// For synthetic bold, we embolden the glyph and render it.
|
||||||
|
if (self.synthetic.bold) {
|
||||||
|
// We need to scale the embolden amount based on the font size.
|
||||||
|
// This is a heuristic I found worked well across a variety of
|
||||||
|
// founts: 1 pixel per 64 units of height.
|
||||||
|
const height: f64 = @floatFromInt(self.face.handle.*.size.*.metrics.height);
|
||||||
|
const ratio: f64 = 64.0 / 2048.0;
|
||||||
|
const amount = @ceil(height * ratio);
|
||||||
|
_ = freetype.c.FT_Outline_Embolden(&glyph.*.outline, @intFromFloat(amount));
|
||||||
|
try self.face.renderGlyph(.normal);
|
||||||
|
}
|
||||||
|
|
||||||
// This bitmap is blank. I've seen it happen in a font, I don't know why.
|
// This bitmap is blank. I've seen it happen in a font, I don't know why.
|
||||||
// If it is empty, we just return a valid glyph struct that does nothing.
|
// If it is empty, we just return a valid glyph struct that does nothing.
|
||||||
|
const bitmap_ft = glyph.*.bitmap;
|
||||||
if (bitmap_ft.rows == 0) return .{
|
if (bitmap_ft.rows == 0) return .{
|
||||||
.width = 0,
|
.width = 0,
|
||||||
.height = 0,
|
.height = 0,
|
||||||
|
Reference in New Issue
Block a user