mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-20 00:18:53 +03:00
font/freetype: disable SVG glyphs, simplify color check
We don't currently support rendering SVG glyphs so they should be ignored when loading. Additionally, the check for whether a glyph is colored has been simplified by just checking the pixel mode of the rendered bitmap. This commit also fixes a bug caused by calling the color check inside of `renderGlyph`, which caused the bitmap to be freed creating a chance for memory corruption and garbled glyphs.
This commit is contained in:
@ -278,7 +278,9 @@ pub const LoadFlags = packed struct {
|
|||||||
color: bool = false,
|
color: bool = false,
|
||||||
compute_metrics: bool = false,
|
compute_metrics: bool = false,
|
||||||
bitmap_metrics_only: bool = false,
|
bitmap_metrics_only: bool = false,
|
||||||
_padding2: u9 = 0,
|
_padding2: u1 = 0,
|
||||||
|
no_svg: bool = false,
|
||||||
|
_padding3: u7 = 0,
|
||||||
|
|
||||||
test {
|
test {
|
||||||
// This must always be an i32 size so we can bitcast directly.
|
// This must always be an i32 size so we can bitcast directly.
|
||||||
|
@ -270,25 +270,21 @@ pub const Face = struct {
|
|||||||
|
|
||||||
/// Returns true if the given glyph ID is colorized.
|
/// Returns true if the given glyph ID is colorized.
|
||||||
pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool {
|
pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool {
|
||||||
// sbix table is always true for now
|
// Load the glyph and see what pixel mode it renders with.
|
||||||
if (self.face.hasSBIX()) return true;
|
// All modes other than BGRA are non-color.
|
||||||
|
// If the glyph fails to load, just return false.
|
||||||
// CBDT/CBLC tables always imply colorized glyphs.
|
|
||||||
// These are used by Noto.
|
|
||||||
if (self.face.hasSfntTable(freetype.Tag.init("CBDT"))) return true;
|
|
||||||
if (self.face.hasSfntTable(freetype.Tag.init("CBLC"))) return true;
|
|
||||||
|
|
||||||
// Otherwise, load the glyph and see what format it is in.
|
|
||||||
self.face.loadGlyph(glyph_id, .{
|
self.face.loadGlyph(glyph_id, .{
|
||||||
.render = true,
|
.render = true,
|
||||||
.color = self.face.hasColor(),
|
.color = self.face.hasColor(),
|
||||||
|
// NO_SVG set to true because we don't currently support rendering
|
||||||
|
// SVG glyphs under FreeType, since that requires bundling another
|
||||||
|
// dependency to handle rendering the SVG.
|
||||||
|
.no_svg = true,
|
||||||
}) catch return false;
|
}) catch return false;
|
||||||
|
|
||||||
// If the glyph is SVG we assume colorized
|
|
||||||
const glyph = self.face.handle.*.glyph;
|
const glyph = self.face.handle.*.glyph;
|
||||||
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_SVG) return true;
|
|
||||||
|
|
||||||
return false;
|
return glyph.*.bitmap.pixel_mode == freetype.c.FT_PIXEL_MODE_BGRA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a glyph using the glyph index. The rendered glyph is stored in the
|
/// Render a glyph using the glyph index. The rendered glyph is stored in the
|
||||||
@ -321,6 +317,11 @@ pub const Face = struct {
|
|||||||
.force_autohint = !self.load_flags.@"force-autohint",
|
.force_autohint = !self.load_flags.@"force-autohint",
|
||||||
.monochrome = !self.load_flags.monochrome,
|
.monochrome = !self.load_flags.monochrome,
|
||||||
.no_autohint = !self.load_flags.autohint,
|
.no_autohint = !self.load_flags.autohint,
|
||||||
|
|
||||||
|
// NO_SVG set to true because we don't currently support rendering
|
||||||
|
// SVG glyphs under FreeType, since that requires bundling another
|
||||||
|
// dependency to handle rendering the SVG.
|
||||||
|
.no_svg = true,
|
||||||
});
|
});
|
||||||
const glyph = self.face.handle.*.glyph;
|
const glyph = self.face.handle.*.glyph;
|
||||||
|
|
||||||
@ -393,12 +394,13 @@ pub const Face = struct {
|
|||||||
const original_width = bitmap_original.width;
|
const original_width = bitmap_original.width;
|
||||||
const original_height = bitmap_original.rows;
|
const original_height = bitmap_original.rows;
|
||||||
var result = bitmap_original;
|
var result = bitmap_original;
|
||||||
// TODO: We are limiting this to only emoji. We can rework this after a future
|
// TODO: We are limiting this to only color glyphs, so mainly emoji.
|
||||||
// improvement (promised by Qwerasd) which implements more flexible resizing rules. For
|
// We can rework this after a future improvement (promised by Qwerasd)
|
||||||
// now, this will suffice
|
// which implements more flexible resizing rules.
|
||||||
if (self.isColorGlyph(glyph_index) and opts.cell_width != null) {
|
if (atlas.format != .grayscale and opts.cell_width != null) {
|
||||||
const cell_width = opts.cell_width orelse unreachable;
|
const cell_width = opts.cell_width orelse unreachable;
|
||||||
// If we have a cell_width, we constrain the glyph to fit within the cell
|
// If we have a cell_width, we constrain
|
||||||
|
// the glyph to fit within the cell(s).
|
||||||
result.width = metrics.cell_width * @as(u32, cell_width);
|
result.width = metrics.cell_width * @as(u32, cell_width);
|
||||||
result.rows = (result.width * original_height) / original_width;
|
result.rows = (result.width * original_height) / original_width;
|
||||||
} else {
|
} else {
|
||||||
@ -743,7 +745,10 @@ pub const Face = struct {
|
|||||||
var c: u8 = ' ';
|
var c: u8 = ' ';
|
||||||
while (c < 127) : (c += 1) {
|
while (c < 127) : (c += 1) {
|
||||||
if (face.getCharIndex(c)) |glyph_index| {
|
if (face.getCharIndex(c)) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{ .render = true })) {
|
if (face.loadGlyph(glyph_index, .{
|
||||||
|
.render = true,
|
||||||
|
.no_svg = true,
|
||||||
|
})) {
|
||||||
max = @max(
|
max = @max(
|
||||||
f26dot6ToF64(face.handle.*.glyph.*.advance.x),
|
f26dot6ToF64(face.handle.*.glyph.*.advance.x),
|
||||||
max,
|
max,
|
||||||
@ -776,7 +781,10 @@ pub const Face = struct {
|
|||||||
break :heights .{
|
break :heights .{
|
||||||
cap: {
|
cap: {
|
||||||
if (face.getCharIndex('H')) |glyph_index| {
|
if (face.getCharIndex('H')) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{ .render = true })) {
|
if (face.loadGlyph(glyph_index, .{
|
||||||
|
.render = true,
|
||||||
|
.no_svg = true,
|
||||||
|
})) {
|
||||||
break :cap f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
break :cap f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
}
|
}
|
||||||
@ -784,7 +792,10 @@ pub const Face = struct {
|
|||||||
},
|
},
|
||||||
ex: {
|
ex: {
|
||||||
if (face.getCharIndex('x')) |glyph_index| {
|
if (face.getCharIndex('x')) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{ .render = true })) {
|
if (face.loadGlyph(glyph_index, .{
|
||||||
|
.render = true,
|
||||||
|
.no_svg = true,
|
||||||
|
})) {
|
||||||
break :ex f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
break :ex f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user